首页 > 解决方案 > Sphinx - 让自定义 HTML 知道变量

问题描述

# conf.py

language='en'
html_extra_path = ["customize.html"]
<!-- customize.html -->

{{ variables }}  <!-- from the conf.py -->
{{ language }}  <!-- expected output: en -->

我怎样才能customize.html知道变量来自配置?

.. note:: 假设customize.html 文件不在主题的文档中。

我可以用Jinja自己做,但这不是我想要的。

我认为sphinx已经提供了一种方法来做这些事情,有人知道它是什么吗?

标签: pythonpython-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脚本


解决方案2:添加插件(扩展)

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。IEapp = Sphinx(...)
  • 应用程序构建
    • 它调用构建器开始构建,由用户定义的构建器格式,在我的例子中构建格式是 HTML,所以它的构建器是StandaloneHTMLBuilder

然后,您知道构建器创建的所有文件。这个想法是:如果我们可以得到 builder,那么我们可以做任何我们想做的事情。

你会找到在 之后创建的构建器Sphinx(...),所以解决方案之一,我在setup_extra_html之后告诉你app = Sphinx(...)

如果您不喜欢编写这些代码并认为它​​太复杂。

第二种方式是写扩展,概念和上面一样——尝试获取builder

你看到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,而是选择命名约定,那么我必须硬编码很多东西。

我希望你能得到帮助并认为它有用!


推荐阅读