Sphinx - 让自定义 HTML 知道变量
Sphinx - Let the customize HTML know the variable
# conf.py
language='en'
html_extra_path = ["customize.html"]
<!-- customize.html -->
{{ variables }} <!-- from the conf.py -->
{{ language }} <!-- expected output: en -->
如何让 customize.html
知道变量来自配置?
..注意::假设customize.html文件不在主题文档中。
我可以用Jinja自己做,但这不是我想要的。
我认为 sphinx 已经提供了一种方法,有人知道它是什么吗?
方案一:修改sphinx-build.exe进程
我破解了代码 (即你不能直接用 sphinx-build.exe 构建) 来实现它。
首先,我们观察sphinx-build.exe
做什么事情。
# site-packages\Sphinx-x.x.x.dist-info\entry_points.txt
[console_scripts]
...
sphinx-build = sphinx.cmd.build:main
...
然后你知道它实际上调用 sphinx.cmd.build:main
到 运行,
可以参考修改,让你满意。
例如:
import sphinx.cmd.build
from sphinx.application import Sphinx
from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.cmd.build import patch_docutils, docutils_namespace, handle_exception, Sphinx
def setup_extra_html(app):
html_builder = app.builder
ctx = {attr: app.config[attr] for attr in dir(app.config) if not attr.startswith('_')}
html_builder.globalcontext = ctx.copy()
# Please put your HTML to the ``templates_path`` that you define, since it concept about the BuiltinTemplateLoader.pathchain
pagename = 'disqus_statistic' # <-- your HTML, you can set it on the conf.py and then get it with ``ctx``
templatename = f'{pagename}.html'
html_builder.handle_page(pagename=pagename, addctx=dict(), templatename=templatename, outfilename=None)
def your_build_main(*args):
...
try:
with patch_docutils(source_dir)), docutils_namespace():
app = Sphinx(...)
if isinstance(app.builder, StandaloneHTMLBuilder):
setup_extra_html(app)
app.build(force_all=False, filenames)
return app.statuscode
except (Exception, KeyboardInterrupt) as exc:
...
cmd_list = [source_dir, output_dir, '-b', 'html', ...]
sphinx.cmd.build.build_main = your_build_main # override it.
sphinx.cmd.build.main(cmd_list) # it will call sphinx.cmd.build.build_main
现在,以下内容将如您所愿。
<!-- original disqus_statistic.html -->
{%- if html_favicon %}
<a href="{{ pathto(html_favicon, 1) }}">Test Icon</a>
{%- endif %}
{{ language }}
代码太长,请自行补全。
或者你可以参考我的脚本 sphinx_cmd_build.py
方案二:添加插件(扩展)
tl;博士
# your_extension.py
from sphinx.application import Sphinx
from sphinx.builders.html import StandaloneHTMLBuilder
import pathlib
def expand_init_builder(app):
Sphinx._init_builder(app)
do_something(app)
def setup(app: Sphinx):
app.add_config_value('config_value_define_by_you', default='', rebuild=True)
app._init_builder = lambda: expand_init_builder(app)
def do_something(app: Sphinx):
user_config = {attr: app.config[attr] for attr in dir(app.config) if not attr.startswith('_')} # all variable of conf.py
# user_config.update(...) # Hard coding is fine, but not recommend.
user_config.update(dict(Path=pathlib.Path)) # recommend you, it's useful.
html_builder: StandaloneHTMLBuilder = app.builder
html_builder.globalcontext = user_config
html_builder.handle_page(pagename=page_name, addctx=dict(), templatename=template_name, outfilename=None)
# conf.py
# sys.path.insert(...)
extensions.append('your_extension') # Make sure your scrips can found in sys.path.
# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-templates_path
templates_path = ['_templates/sphinx_rtd_theme'] # I hope you know what I mean... see the above link.
# I put my_html.html in ``_templates/sphinx_rtd_theme/my_html.html``
config_value_define_by_you = "https://github.com/CarsonSlovoka/typing-game"
<!-- my_html.html -->
{{ Path(config_value_define_by_you).name }} <!-- render result: typing-game -->
长话短说(解释解决方案2)
Sphinx-build.exe在做什么事情?
- init # <-- 我们不关心这个。
- 创建一个Sphinx()实例,实例为app。即
app = Sphinx(...)
- app.build
- 它调用构建器开始构建,构建器格式由用户定义,在我的例子中构建格式是HTML,所以它的构建器是
StandaloneHTMLBuilder
然后,您知道构建器创建的所有文件。
这个想法是:如果我们可以 获得构建器 ,那么我们可以做任何我们想做的事情。
您会发现在 Sphinx(...)
之后创建的生成器,
所以解决方案之一,我在 app = Sphinx(...)
之后告诉你 setup_extra_html
如果你不喜欢写这些代码,觉得太复杂了。
第二种方式是写扩展,
概念与上述相同——尝试 获取生成器
你看到了Sphinx(...)
它的构造函数,你发现代码如下
class Sphinx:
def __init__(...):
...
# load all user-given extension modules
for extension in self.config.extensions:
self.setup_extension(extension) # <-- the extension you write
...
# create the builder
self.builder = self.create_builder(buildername) <-- this is we want
self._init_env(freshenv)
self._init_builder()
然后,您知道创建无法获取生成器的扩展的正常方法,
但是你注意到,如果你在 self._init_builder() 完成后做某事,那就没问题了。
我提供我的项目供您参考。
我真正想要的是,我想创建一个页面,我希望它可以计算每篇文章的评论数,并显示给我。
你会明白,如果我不使用 sphinx,而是选择命名约定,那么我必须硬编码很多东西。
- 解决方案 1:https://github.com/CarsonSlovoka/typing-game/commit/376c9e20eac4a3c9b269b5bfbc8adb85ad9f6d36
- 解决方案 2:https://github.com/CarsonSlovoka/typing-game/commit/69411e15f1ace853edcafafc14759ba79b7ac288
- demo: https://carsonslovoka.github.io/typing-game/en/doc.html#statistic -> 然后,点击 Disqus 的计数。
希望对您有所帮助,觉得有用!
# conf.py
language='en'
html_extra_path = ["customize.html"]
<!-- customize.html -->
{{ variables }} <!-- from the conf.py -->
{{ language }} <!-- expected output: en -->
如何让 customize.html
知道变量来自配置?
..注意::假设customize.html文件不在主题文档中。
我可以用Jinja自己做,但这不是我想要的。
我认为 sphinx 已经提供了一种方法,有人知道它是什么吗?
方案一:修改sphinx-build.exe进程
我破解了代码 (即你不能直接用 sphinx-build.exe 构建) 来实现它。
首先,我们观察sphinx-build.exe
做什么事情。
# site-packages\Sphinx-x.x.x.dist-info\entry_points.txt
[console_scripts]
...
sphinx-build = sphinx.cmd.build:main
...
然后你知道它实际上调用 sphinx.cmd.build:main
到 运行,
可以参考修改,让你满意。
例如:
import sphinx.cmd.build
from sphinx.application import Sphinx
from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.cmd.build import patch_docutils, docutils_namespace, handle_exception, Sphinx
def setup_extra_html(app):
html_builder = app.builder
ctx = {attr: app.config[attr] for attr in dir(app.config) if not attr.startswith('_')}
html_builder.globalcontext = ctx.copy()
# Please put your HTML to the ``templates_path`` that you define, since it concept about the BuiltinTemplateLoader.pathchain
pagename = 'disqus_statistic' # <-- your HTML, you can set it on the conf.py and then get it with ``ctx``
templatename = f'{pagename}.html'
html_builder.handle_page(pagename=pagename, addctx=dict(), templatename=templatename, outfilename=None)
def your_build_main(*args):
...
try:
with patch_docutils(source_dir)), docutils_namespace():
app = Sphinx(...)
if isinstance(app.builder, StandaloneHTMLBuilder):
setup_extra_html(app)
app.build(force_all=False, filenames)
return app.statuscode
except (Exception, KeyboardInterrupt) as exc:
...
cmd_list = [source_dir, output_dir, '-b', 'html', ...]
sphinx.cmd.build.build_main = your_build_main # override it.
sphinx.cmd.build.main(cmd_list) # it will call sphinx.cmd.build.build_main
现在,以下内容将如您所愿。
<!-- original disqus_statistic.html -->
{%- if html_favicon %}
<a href="{{ pathto(html_favicon, 1) }}">Test Icon</a>
{%- endif %}
{{ language }}
代码太长,请自行补全。 或者你可以参考我的脚本 sphinx_cmd_build.py
方案二:添加插件(扩展)
tl;博士
# your_extension.py
from sphinx.application import Sphinx
from sphinx.builders.html import StandaloneHTMLBuilder
import pathlib
def expand_init_builder(app):
Sphinx._init_builder(app)
do_something(app)
def setup(app: Sphinx):
app.add_config_value('config_value_define_by_you', default='', rebuild=True)
app._init_builder = lambda: expand_init_builder(app)
def do_something(app: Sphinx):
user_config = {attr: app.config[attr] for attr in dir(app.config) if not attr.startswith('_')} # all variable of conf.py
# user_config.update(...) # Hard coding is fine, but not recommend.
user_config.update(dict(Path=pathlib.Path)) # recommend you, it's useful.
html_builder: StandaloneHTMLBuilder = app.builder
html_builder.globalcontext = user_config
html_builder.handle_page(pagename=page_name, addctx=dict(), templatename=template_name, outfilename=None)
# conf.py
# sys.path.insert(...)
extensions.append('your_extension') # Make sure your scrips can found in sys.path.
# https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-templates_path
templates_path = ['_templates/sphinx_rtd_theme'] # I hope you know what I mean... see the above link.
# I put my_html.html in ``_templates/sphinx_rtd_theme/my_html.html``
config_value_define_by_you = "https://github.com/CarsonSlovoka/typing-game"
<!-- my_html.html -->
{{ Path(config_value_define_by_you).name }} <!-- render result: typing-game -->
长话短说(解释解决方案2)
Sphinx-build.exe在做什么事情?
- init # <-- 我们不关心这个。
- 创建一个Sphinx()实例,实例为app。即
app = Sphinx(...)
- app.build
- 它调用构建器开始构建,构建器格式由用户定义,在我的例子中构建格式是HTML,所以它的构建器是
StandaloneHTMLBuilder
- 它调用构建器开始构建,构建器格式由用户定义,在我的例子中构建格式是HTML,所以它的构建器是
然后,您知道构建器创建的所有文件。 这个想法是:如果我们可以 获得构建器 ,那么我们可以做任何我们想做的事情。
您会发现在 Sphinx(...)
之后创建的生成器,
所以解决方案之一,我在 app = Sphinx(...)
setup_extra_html
如果你不喜欢写这些代码,觉得太复杂了。
第二种方式是写扩展, 概念与上述相同——尝试 获取生成器
你看到了Sphinx(...)
它的构造函数,你发现代码如下
class Sphinx:
def __init__(...):
...
# load all user-given extension modules
for extension in self.config.extensions:
self.setup_extension(extension) # <-- the extension you write
...
# create the builder
self.builder = self.create_builder(buildername) <-- this is we want
self._init_env(freshenv)
self._init_builder()
然后,您知道创建无法获取生成器的扩展的正常方法, 但是你注意到,如果你在 self._init_builder() 完成后做某事,那就没问题了。
我提供我的项目供您参考。 我真正想要的是,我想创建一个页面,我希望它可以计算每篇文章的评论数,并显示给我。 你会明白,如果我不使用 sphinx,而是选择命名约定,那么我必须硬编码很多东西。
- 解决方案 1:https://github.com/CarsonSlovoka/typing-game/commit/376c9e20eac4a3c9b269b5bfbc8adb85ad9f6d36
- 解决方案 2:https://github.com/CarsonSlovoka/typing-game/commit/69411e15f1ace853edcafafc14759ba79b7ac288
- demo: https://carsonslovoka.github.io/typing-game/en/doc.html#statistic -> 然后,点击 Disqus 的计数。
希望对您有所帮助,觉得有用!