summaryrefslogtreecommitdiff
path: root/sphinx
diff options
context:
space:
mode:
authorgbrandl <devnull@localhost>2009-01-05 20:22:30 +0100
committergbrandl <devnull@localhost>2009-01-05 20:22:30 +0100
commit5d46ac4e9e56bc4ce5ea3a5241fa243bf4e05e5e (patch)
treedf3c301170c2390a0234e3e3f60335202f5befdf /sphinx
parent9d7be98a2d69b979d8c1be5b408ba15c657b37b6 (diff)
downloadsphinx-5d46ac4e9e56bc4ce5ea3a5241fa243bf4e05e5e.tar.gz
Basic theme infrastructure.
Diffstat (limited to 'sphinx')
-rw-r--r--sphinx/builders/__init__.py10
-rw-r--r--sphinx/builders/html.py4
-rw-r--r--sphinx/config.py4
-rw-r--r--sphinx/jinja2glue.py81
-rw-r--r--sphinx/themes/basic/theme.conf4
-rw-r--r--sphinx/themes/default/theme.conf3
-rw-r--r--sphinx/themes/sphinxdoc/theme.conf3
-rw-r--r--sphinx/themes/traditional/theme.conf3
-rw-r--r--sphinx/theming.py75
9 files changed, 132 insertions, 55 deletions
diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py
index 8847b6dc..5179cba0 100644
--- a/sphinx/builders/__init__.py
+++ b/sphinx/builders/__init__.py
@@ -72,16 +72,20 @@ class Builder(object):
def init_templates(self):
"""
- Initialize the template system.
+ Initialize the theme and template system.
Call this method from init() if you need templates in your builder.
"""
+ from sphinx.theming import Theme
+ Theme.init_themes(self)
+ self.theme = Theme(self.config.html_theme)
+
if self.config.template_bridge:
self.templates = self.app.import_object(
self.config.template_bridge, 'template_bridge setting')()
else:
- from sphinx.jinja2glue import BuiltinTemplates
- self.templates = BuiltinTemplates()
+ from sphinx.jinja2glue import BuiltinTemplateLoader
+ self.templates = BuiltinTemplateLoader()
self.templates.init(self)
def get_target_uri(self, docname, typ=None):
diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py
index 91682aa2..55cf2de2 100644
--- a/sphinx/builders/html.py
+++ b/sphinx/builders/html.py
@@ -62,7 +62,6 @@ class StandaloneHTMLBuilder(Builder):
script_files = ['_static/jquery.js', '_static/doctools.js']
def init(self):
- """Load templates."""
self.init_templates()
self.init_translator_class()
if self.config.html_file_suffix:
@@ -382,7 +381,8 @@ class StandaloneHTMLBuilder(Builder):
shutil.copyfile(jsfile, path.join(self.outdir, '_static',
'translations.js'))
# then, copy over all user-supplied static files
- staticdirnames = [path.join(package_dir, 'static')] + \
+ staticdirnames = [path.join(themepath, 'static')
+ for themepath in self.theme.get_dirchain()[::-1]] + \
[path.join(self.confdir, spath)
for spath in self.config.html_static_path]
for staticdirname in staticdirnames:
diff --git a/sphinx/config.py b/sphinx/config.py
index 428c0bf4..a3ae34c5 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -53,11 +53,13 @@ class Config(object):
keep_warnings = (False, True),
# HTML options
+ html_theme = ('default', False),
+ html_theme_path = ([], False),
html_title = (lambda self: '%s v%s documentation' %
(self.project, self.release),
False),
html_short_title = (lambda self: self.html_title, False),
- html_style = ('default.css', False),
+ html_style = ('default.css', False), # XXX
html_logo = (None, False),
html_favicon = (None, False),
html_static_path = ([], False),
diff --git a/sphinx/jinja2glue.py b/sphinx/jinja2glue.py
index 0c7c5d72..fc3efc01 100644
--- a/sphinx/jinja2glue.py
+++ b/sphinx/jinja2glue.py
@@ -18,63 +18,31 @@ from sphinx.util import mtimes_of_files
from sphinx.application import TemplateBridge
-class SphinxLoader(jinja2.BaseLoader):
+class BuiltinTemplateLoader(TemplateBridge, jinja2.BaseLoader):
"""
- A jinja2 reimplementation of `sphinx._jinja.SphinxFileSystemLoader`.
+ Interfaces the rendering environment of jinja2 for use in Sphinx.
"""
- def __init__(self, basepath, extpaths, encoding='utf-8'):
- """
- Create a new loader for sphinx.
+ # TemplateBridge interface
- *extpaths* is a list of directories, which provide additional templates
- to sphinx.
-
- *encoding* is used to decode the templates into unicode strings.
- Defaults to utf-8.
-
- If *basepath* is set, this path is used to load sphinx core templates.
- If False, these templates are loaded from the sphinx package.
- """
- self.core_loader = jinja2.FileSystemLoader(basepath)
- self.all_loaders = jinja2.ChoiceLoader(
- [jinja2.FileSystemLoader(extpath) for extpath in extpaths] +
- [self.core_loader])
+ def init(self, builder):
+ self.theme = builder.theme
+ # create a chain of paths to search:
+ # the theme's own dir and its bases' dirs
+ chain = self.theme.get_dirchain()
+ # then the theme parent paths (XXX doc)
+ chain.extend(self.theme.themepath)
- def get_source(self, environment, template):
- # exclamation mark forces loading from core
- if template.startswith('!'):
- return self.core_loader.get_source(environment, template[1:])
- # check if the template is probably an absolute path
- fs_path = template.replace('/', path.sep)
- if path.isabs(fs_path):
- if not path.exists(fs_path):
- raise jinja2.TemplateNotFound(template)
- f = codecs.open(fs_path, 'r', self.encoding)
- try:
- mtime = path.getmtime(path)
- return (f.read(), fs_path,
- lambda: mtime == path.getmtime(path))
- finally:
- f.close()
- # finally try to load from custom templates
- return self.all_loaders.get_source(environment, template)
+ # prepend explicit template paths
+ if builder.config.templates_path:
+ chain[0:0] = builder.config.templates_path
+ # make the paths into loaders
+ self.loaders = map(jinja2.FileSystemLoader, chain)
-class BuiltinTemplates(TemplateBridge):
- """
- Interfaces the rendering environment of jinja2 for use in sphinx.
- """
-
- def init(self, builder):
- base_templates_path = path.join(path.dirname(__file__), 'templates')
- ext_templates_path = [path.join(builder.confdir, dir)
- for dir in builder.config.templates_path]
- self.templates_path = [base_templates_path] + ext_templates_path
- loader = SphinxLoader(base_templates_path, ext_templates_path)
use_i18n = builder.translator is not None
extensions = use_i18n and ['jinja2.ext.i18n'] or []
- self.environment = jinja2.Environment(loader=loader,
+ self.environment = jinja2.Environment(loader=self,
extensions=extensions)
if use_i18n:
self.environment.install_gettext_translations(builder.translator)
@@ -83,4 +51,19 @@ class BuiltinTemplates(TemplateBridge):
return self.environment.get_template(template).render(context)
def newest_template_mtime(self):
- return max(mtimes_of_files(self.templates_path, '.html'))
+ return max(mtimes_of_files(self.theme.themepath, '.html'))
+
+ # Loader interface
+
+ def get_source(self, environment, template):
+ loaders = self.loaders
+ # exclamation mark starts search from base
+ if template.startswith('!'):
+ loaders = loaders[1:]
+ template = template[1:]
+ for loader in loaders:
+ try:
+ return loader.get_source(environment, template)
+ except jinja2.TemplateNotFound:
+ pass
+ raise jinja2.TemplateNotFound(template)
diff --git a/sphinx/themes/basic/theme.conf b/sphinx/themes/basic/theme.conf
new file mode 100644
index 00000000..160cac40
--- /dev/null
+++ b/sphinx/themes/basic/theme.conf
@@ -0,0 +1,4 @@
+[theme]
+inherit = none
+stylesheet = basic.css
+pygments_style = sphinx
diff --git a/sphinx/themes/default/theme.conf b/sphinx/themes/default/theme.conf
new file mode 100644
index 00000000..0d5ab8eb
--- /dev/null
+++ b/sphinx/themes/default/theme.conf
@@ -0,0 +1,3 @@
+[theme]
+inherit = basic
+stylesheet = default.css
diff --git a/sphinx/themes/sphinxdoc/theme.conf b/sphinx/themes/sphinxdoc/theme.conf
new file mode 100644
index 00000000..cd74cb8a
--- /dev/null
+++ b/sphinx/themes/sphinxdoc/theme.conf
@@ -0,0 +1,3 @@
+[theme]
+inherit = basic
+stylesheet = sphinxdoc.css
diff --git a/sphinx/themes/traditional/theme.conf b/sphinx/themes/traditional/theme.conf
new file mode 100644
index 00000000..02b77833
--- /dev/null
+++ b/sphinx/themes/traditional/theme.conf
@@ -0,0 +1,3 @@
+[theme]
+inherit = basic
+stylesheet = traditional.css
diff --git a/sphinx/theming.py b/sphinx/theming.py
new file mode 100644
index 00000000..9493481c
--- /dev/null
+++ b/sphinx/theming.py
@@ -0,0 +1,75 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.theming
+ ~~~~~~~~~~~~~~
+
+ Theming support for HTML builders.
+
+ :copyright: 2007-2009 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import os
+import ConfigParser
+from os import path
+
+from sphinx.application import SphinxError
+
+
+THEMECONF = 'theme.conf'
+
+class ThemeError(SphinxError):
+ category = 'Theme error'
+
+
+class Theme(object):
+ """
+ Represents the theme chosen in the configuration.
+ """
+ @classmethod
+ def init_themes(cls, builder):
+ """Search all theme paths for available themes."""
+ cls.themes = {}
+
+ cls.themepath = list(builder.config.html_theme_path)
+ cls.themepath.append(
+ path.join(path.abspath(path.dirname(__file__)), 'themes'))
+
+ for themedir in cls.themepath[::-1]:
+ themedir = path.join(builder.confdir, themedir)
+ if not path.isdir(themedir):
+ continue
+ for theme in os.listdir(themedir):
+ if not path.isfile(path.join(themedir, theme, THEMECONF)):
+ continue
+ cls.themes[theme] = path.join(themedir, theme)
+
+ def __init__(self, name):
+ if name not in self.themes:
+ raise ThemeError('no theme named %r found' % name)
+ self.name = name
+ self.themedir = self.themes[name]
+
+ self.themeconf = ConfigParser.RawConfigParser()
+ self.themeconf.read(path.join(self.themedir, THEMECONF))
+
+ inherit = self.themeconf.get('theme', 'inherit')
+ if inherit == 'none':
+ self.base = None
+ elif inherit not in self.themes:
+ raise ThemeError('no theme named %r found, inherited by %r' %
+ (inherit, name))
+ else:
+ self.base = Theme(inherit)
+
+ def get_dirchain(self):
+ """
+ Return a list of theme directories, beginning with this theme's,
+ then the base theme's, then that one's base theme's, etc.
+ """
+ chain = [self.themedir]
+ base = self.base
+ while base is not None:
+ chain.append(base.themedir)
+ base = base.base
+ return chain