mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Basic theme infrastructure.
This commit is contained in:
parent
249f9544b7
commit
beb987bd5b
@ -75,7 +75,8 @@ pygments_style = 'friendly'
|
|||||||
# The style sheet to use for HTML and HTML Help pages. A file of that name
|
# The style sheet to use for HTML and HTML Help pages. A file of that name
|
||||||
# must exist either in Sphinx' static/ path, or in one of the custom paths
|
# must exist either in Sphinx' static/ path, or in one of the custom paths
|
||||||
# given in html_static_path.
|
# given in html_static_path.
|
||||||
html_style = 'sphinxdoc.css'
|
#html_style = 'sphinxdoc.css'
|
||||||
|
html_theme = 'sphinxdoc'
|
||||||
|
|
||||||
# Add any paths that contain custom static files (such as style sheets) here,
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
# relative to this directory. They are copied after the builtin static files,
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
|
@ -72,16 +72,20 @@ class Builder(object):
|
|||||||
|
|
||||||
def init_templates(self):
|
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.
|
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:
|
if self.config.template_bridge:
|
||||||
self.templates = self.app.import_object(
|
self.templates = self.app.import_object(
|
||||||
self.config.template_bridge, 'template_bridge setting')()
|
self.config.template_bridge, 'template_bridge setting')()
|
||||||
else:
|
else:
|
||||||
from sphinx.jinja2glue import BuiltinTemplates
|
from sphinx.jinja2glue import BuiltinTemplateLoader
|
||||||
self.templates = BuiltinTemplates()
|
self.templates = BuiltinTemplateLoader()
|
||||||
self.templates.init(self)
|
self.templates.init(self)
|
||||||
|
|
||||||
def get_target_uri(self, docname, typ=None):
|
def get_target_uri(self, docname, typ=None):
|
||||||
|
@ -62,7 +62,6 @@ class StandaloneHTMLBuilder(Builder):
|
|||||||
script_files = ['_static/jquery.js', '_static/doctools.js']
|
script_files = ['_static/jquery.js', '_static/doctools.js']
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
"""Load templates."""
|
|
||||||
self.init_templates()
|
self.init_templates()
|
||||||
self.init_translator_class()
|
self.init_translator_class()
|
||||||
if self.config.html_file_suffix:
|
if self.config.html_file_suffix:
|
||||||
@ -382,7 +381,8 @@ class StandaloneHTMLBuilder(Builder):
|
|||||||
shutil.copyfile(jsfile, path.join(self.outdir, '_static',
|
shutil.copyfile(jsfile, path.join(self.outdir, '_static',
|
||||||
'translations.js'))
|
'translations.js'))
|
||||||
# then, copy over all user-supplied static files
|
# 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)
|
[path.join(self.confdir, spath)
|
||||||
for spath in self.config.html_static_path]
|
for spath in self.config.html_static_path]
|
||||||
for staticdirname in staticdirnames:
|
for staticdirname in staticdirnames:
|
||||||
|
@ -53,11 +53,13 @@ class Config(object):
|
|||||||
keep_warnings = (False, True),
|
keep_warnings = (False, True),
|
||||||
|
|
||||||
# HTML options
|
# HTML options
|
||||||
|
html_theme = ('default', False),
|
||||||
|
html_theme_path = ([], False),
|
||||||
html_title = (lambda self: '%s v%s documentation' %
|
html_title = (lambda self: '%s v%s documentation' %
|
||||||
(self.project, self.release),
|
(self.project, self.release),
|
||||||
False),
|
False),
|
||||||
html_short_title = (lambda self: self.html_title, 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_logo = (None, False),
|
||||||
html_favicon = (None, False),
|
html_favicon = (None, False),
|
||||||
html_static_path = ([], False),
|
html_static_path = ([], False),
|
||||||
|
@ -18,63 +18,31 @@ from sphinx.util import mtimes_of_files
|
|||||||
from sphinx.application import TemplateBridge
|
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'):
|
# TemplateBridge interface
|
||||||
"""
|
|
||||||
Create a new loader for sphinx.
|
|
||||||
|
|
||||||
*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 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)
|
|
||||||
|
|
||||||
|
|
||||||
class BuiltinTemplates(TemplateBridge):
|
|
||||||
"""
|
|
||||||
Interfaces the rendering environment of jinja2 for use in sphinx.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def init(self, builder):
|
def init(self, builder):
|
||||||
base_templates_path = path.join(path.dirname(__file__), 'templates')
|
self.theme = builder.theme
|
||||||
ext_templates_path = [path.join(builder.confdir, dir)
|
# create a chain of paths to search:
|
||||||
for dir in builder.config.templates_path]
|
# the theme's own dir and its bases' dirs
|
||||||
self.templates_path = [base_templates_path] + ext_templates_path
|
chain = self.theme.get_dirchain()
|
||||||
loader = SphinxLoader(base_templates_path, ext_templates_path)
|
# then the theme parent paths (XXX doc)
|
||||||
|
chain.extend(self.theme.themepath)
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
use_i18n = builder.translator is not None
|
use_i18n = builder.translator is not None
|
||||||
extensions = use_i18n and ['jinja2.ext.i18n'] or []
|
extensions = use_i18n and ['jinja2.ext.i18n'] or []
|
||||||
self.environment = jinja2.Environment(loader=loader,
|
self.environment = jinja2.Environment(loader=self,
|
||||||
extensions=extensions)
|
extensions=extensions)
|
||||||
if use_i18n:
|
if use_i18n:
|
||||||
self.environment.install_gettext_translations(builder.translator)
|
self.environment.install_gettext_translations(builder.translator)
|
||||||
@ -83,4 +51,19 @@ class BuiltinTemplates(TemplateBridge):
|
|||||||
return self.environment.get_template(template).render(context)
|
return self.environment.get_template(template).render(context)
|
||||||
|
|
||||||
def newest_template_mtime(self):
|
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)
|
||||||
|
4
sphinx/themes/basic/theme.conf
Normal file
4
sphinx/themes/basic/theme.conf
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[theme]
|
||||||
|
inherit = none
|
||||||
|
stylesheet = basic.css
|
||||||
|
pygments_style = sphinx
|
3
sphinx/themes/default/theme.conf
Normal file
3
sphinx/themes/default/theme.conf
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[theme]
|
||||||
|
inherit = basic
|
||||||
|
stylesheet = default.css
|
3
sphinx/themes/sphinxdoc/theme.conf
Normal file
3
sphinx/themes/sphinxdoc/theme.conf
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[theme]
|
||||||
|
inherit = basic
|
||||||
|
stylesheet = sphinxdoc.css
|
3
sphinx/themes/traditional/theme.conf
Normal file
3
sphinx/themes/traditional/theme.conf
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[theme]
|
||||||
|
inherit = basic
|
||||||
|
stylesheet = traditional.css
|
75
sphinx/theming.py
Normal file
75
sphinx/theming.py
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user