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

如何在Sphinx中为不同内容区域配置自定义章节编号?

Customizing Sphinx Section Numbering with Differentiated Formats

First off, I get why you're frustrated—Sphinx's default section numbering is pretty rigid, and the toctree system doesn't make it obvious how to customize per-section number formats. Let's break down how to extend the TocTreeCollector properly to get that i/ii, 1.1, A/B numbering setup you want.

Core Problem Recap

Your initial attempt with a _to_alpha function in _walk_toc only modified page-level section numbers, not the numbered entries in the toctree itself. That's because the assign_section_numbers method in TocTreeCollector handles the global number stack for all toctree entries, and we need to hook into that logic to apply format-specific rules per toctree.

Step-by-Step Solution

1. Create a Custom Sphinx Extension

We'll build a small extension that:

  • Adds a new :format: option to the toctree directive
  • Replaces the default TocTreeCollector with a custom version that respects the format option
  • Implements number conversion functions for roman numerals, arabic, and uppercase letters

Create a file named custom_toc_numbering.py in your Sphinx project root:

import string
from sphinx.environment.collectors.toctree import TocTreeCollector
from sphinx.util.roman import toRoman
from sphinx.application import Sphinx
from sphinx.directives.other import TocTree as BaseTocTree

# Number format conversion functions
def _to_lower_roman(numstack):
    return [toRoman(num).lower() for num in numstack]

def _to_arabic(numstack):
    return [str(num) for num in numstack]

def _to_upper_letters(numstack):
    # For appendices: convert top-level numbers to letters, keep sub-levels as arabic
    # Example: [1, 2] → ['A', '2'] → "A.2"
    return [
        string.ascii_uppercase[num - 1] if i == 0 else str(num)
        for i, num in enumerate(numstack)
    ]

# Custom TocTreeCollector with format-aware numbering
class CustomTocTreeCollector(TocTreeCollector):
    def assign_section_numbers(self, app: Sphinx, doctree):
        # First run the default numbering logic to build the base number stack
        super().assign_section_numbers(app, doctree)

        # Track which documents belong to which toctree format
        doc_format_map = {}

        # Parse all toctree nodes to map docs to their format
        for toctree_node in doctree.findall(lambda n: n.tagname == 'toctree'):
            toc_format = toctree_node.get('format', 'arabic')
            # Map each entry in the toctree to its format
            for entry in toctree_node['entries']:
                docname = entry[1] if isinstance(entry, tuple) else entry
                if docname:
                    doc_format_map[docname] = toc_format

        # Update section numbers for all documents based on their format
        for docname, toc in app.env.tocs.items():
            if docname not in doc_format_map:
                continue  # Skip docs not in a formatted toctree

            target_format = doc_format_map[docname]
            # Traverse all sections in the document's TOC
            for section in toc.findall(lambda n: n.tagname == 'section'):
                if 'number' not in section:
                    continue

                numstack = section['number']
                # Apply the correct format conversion
                if target_format == 'lowercase-roman':
                    section['number'] = _to_lower_roman(numstack)
                elif target_format == 'uppercase-letters':
                    section['number'] = _to_upper_letters(numstack)
                # Arabic is default, no change needed

                # Update the display text for the number
                section['number_text'] = '.'.join(section['number'])

        # Update the global toc_numbers registry (used for sidebar/refs)
        for docname, numstack in app.env.toc_numbers.items():
            if docname in doc_format_map:
                target_format = doc_format_map[docname]
                if target_format == 'lowercase-roman':
                    app.env.toc_numbers[docname] = _to_lower_roman(numstack)
                elif target_format == 'uppercase-letters':
                    app.env.toc_numbers[docname] = _to_upper_letters(numstack)

# Extend the default TocTree directive to accept the :format: option
class CustomTocTree(BaseTocTree):
    option_spec = dict(BaseTocTree.option_spec)
    option_spec['format'] = str

def setup(app: Sphinx):
    # Replace the default TocTreeCollector with our custom one
    app.env.collectors['toctree'] = CustomTocTreeCollector(app)
    # Register our extended TocTree directive
    app.add_directive('toctree', CustomTocTree)

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

2. Enable the Extension in conf.py

Add the extension to your Sphinx configuration:

extensions = [
    # ... your existing extensions
    'custom_toc_numbering',
]

3. Use the New :format: Option in Your Toctrees

Now you can define different number formats for each section of your docs:

.. toctree::
   :caption: 前言
   :format: lowercase-roman
   :numbered:

   foreword/intro
   foreword/overview

.. toctree::
   :caption: 主内容
   :format: arabic
   :numbered:

   chapter1/index
   chapter2/index

.. toctree::
   :caption: 附录
   :format: uppercase-letters
   :numbered:

   appendix/a
   appendix/b

Key Notes

  • Roman Numerals: We use Sphinx's built-in toRoman utility to avoid reinventing the wheel.
  • Multi-Level Numbering: The _to_upper_letters function keeps sub-level numbers as arabic (e.g., A.1, A.2) which is standard for appendices. Adjust this function if you need a different behavior.
  • Sidebar/Reference Updates: The custom collector updates both the document section numbers and the global toc_numbers registry, so numbered references and sidebar TOCs will show the correct formatted numbers.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 06:32:31