You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现可嵌套service_card的Sphinx service_card_wrapper指令?

问题:实现可嵌套service_card的Sphinx指令service_card_wrapper

我们需要创建一个Sphinx指令service_card_wrapper,支持嵌套已实现的service_card指令,示例用法如下:

Directives
==========

.. service_card_wrapper::

    .. service_card::

    .. service_card::

目前service_card指令已实现,但无法将其内容渲染到service_card_wrapper中,期望生成的HTML结构为:

<div class="service-card-wrapper">
  <div class="service-card">
    Content 1
  </div>
  <div class="service-card">
    Content 2
  </div>
</div>

已实现的service_card指令代码如下:

from docutils import nodes

from docutils.parsers.rst import Directive
from docutils.parsers.rst import directives
from sphinx.util import logging


LOG = logging.getLogger(__name__)


class service_card(nodes.General, nodes.Element):
    pass


class ServiceCard(Directive):
    node_class = service_card
    option_spec = {
        # 'service_type': directives.unchanged_required,
    }

    has_content = False

    def run(self):
        node = self.node_class()
        # node['service_type'] = self.options.get('service_type')
        return [node]

def service_card_html(self, node):
    # This method renders containers per each service of the category with all
    # links as individual list items
    data = '''
        <div class="card">
        <h5 class="card-header">Featured</h5>
        <div class="card-body">
        <h5 class="card-title">Special title treatment</h5>
        <p class="card-text">With supporting text below as a natural lead-in to additional content.</p>
        <a href="#" class="btn btn-primary">Go somewhere</a>
        </div>
        </div>'''
    self.body.append(data)
    raise nodes.SkipNode

def setup(app):
    app.add_node(service_card,
                 html=(service_card_html, None))
    app.add_directive("service_card", ServiceCard)

    return {
        'version': '0.1',
        'parallel_read_safe': True,
        'parallel_write_safe': True,
    }

解决方案

以下是完整的实现代码,包含service_card_wrapper的定义、渲染逻辑,同时调整了service_card的HTML类名以匹配期望结构:

from docutils import nodes

from docutils.parsers.rst import Directive
from docutils.parsers.rst import directives
from sphinx.util import logging


LOG = logging.getLogger(__name__)


class service_card(nodes.General, nodes.Element):
    pass


class service_card_wrapper(nodes.General, nodes.Element):
    pass


class ServiceCard(Directive):
    node_class = service_card
    option_spec = {
        # 'service_type': directives.unchanged_required,
    }

    has_content = False

    def run(self):
        node = self.node_class()
        # node['service_type'] = self.options.get('service_type')
        return [node]


class ServiceCardWrapper(Directive):
    node_class = service_card_wrapper
    has_content = True  # 允许指令包含嵌套内容
    option_spec = {}

    def run(self):
        node = self.node_class()
        # 解析嵌套的内容,生成子节点并添加到wrapper节点中
        self.state.nested_parse(self.content, self.content_offset, node)
        return [node]


def service_card_html(self, node):
    # 修改类名为service-card,匹配期望的HTML结构
    data = '''
        <div class="service-card">
        <h5 class="card-header">Featured</h5>
        <div class="card-body">
        <h5 class="card-title">Special title treatment</h5>
        <p class="card-text">With supporting text below as a natural lead-in to additional content.</p>
        <a href="#" class="btn btn-primary">Go somewhere</a>
        </div>
        </div>'''
    self.body.append(data)
    raise nodes.SkipNode


def service_card_wrapper_html(self, node):
    # 添加wrapper起始标签
    self.body.append('<div class="service-card-wrapper">')
    # 渲染所有子节点(即嵌套的service_card)
    self.visit_children(node)
    # 添加wrapper结束标签
    self.body.append('</div>')
    raise nodes.SkipNode


def setup(app):
    app.add_node(service_card, html=(service_card_html, None))
    app.add_node(service_card_wrapper, html=(service_card_wrapper_html, None))
    
    app.add_directive("service_card", ServiceCard)
    app.add_directive("service_card_wrapper", ServiceCardWrapper)

    return {
        'version': '0.1',
        'parallel_read_safe': True,
        'parallel_write_safe': True,
    }

关键说明

  1. ServiceCardWrapper指令核心:设置has_content=True,并通过self.state.nested_parse解析指令内的嵌套内容,将生成的子节点挂载到service_card_wrapper节点上
  2. wrapper渲染逻辑:service_card_wrapper_html函数先输出容器起始标签,再调用self.visit_children(node)触发所有子节点的渲染,最后输出结束标签,确保子节点的HTML被正确包裹
  3. 类名调整:将service_card渲染的HTML类从card改为service-card,完全匹配期望的结构

内容的提问来源于stack exchange,提问作者allegoric

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 22:43:18