考虑一个带有此骨架的reStructuredText文档:
Main Title
==========
text text text text text
Subsection
----------
text text text text text
.. my-import-from:: file1
.. my-import-from:: file2
my-import-from
指令由特定于文档的Sphinx扩展提供,该扩展应该读取作为其参数提供的文件,解析嵌入其中的reST,并将结果作为当前输入文件中的一部分注入。 (就像autodoc一样,但是对于不同的文件格式。)我现在的代码就像这样:
class MyImportFromDirective(Directive):
required_arguments = 1
def run(self):
src, srcline = self.state_machine.get_source_and_line()
doc_file = os.path.normpath(os.path.join(os.path.dirname(src),
self.arguments[0]))
self.state.document.settings.record_dependencies.add(doc_file)
doc_text = ViewList()
try:
doc_text = extract_doc_from_file(doc_file)
except EnvironmentError as e:
raise self.error(e.filename + ": " + e.strerror) from e
doc_section = nodes.section()
doc_section.document = self.state.document
# report line numbers within the nested parse correctly
old_reporter = self.state.memo.reporter
self.state.memo.reporter = AutodocReporter(doc_text,
self.state.memo.reporter)
nested_parse_with_titles(self.state, doc_text, doc_section)
self.state.memo.reporter = old_reporter
if len(doc_section) == 1 and isinstance(doc_section[0], nodes.section):
doc_section = doc_section[0]
# If there was no title, synthesize one from the name of the file.
if len(doc_section) == 0 or not isinstance(doc_section[0], nodes.title):
doc_title = nodes.title()
doc_title.append(make_title_text(doc_file))
doc_section.insert(0, doc_title)
return [doc_section]
这是有效的,除了,新部分被注入当前部分的子,而不是兄弟。换句话说,上面的示例文档生成了这样的TOC树:
- 主题
- 第
- File1中
- 文件2
而不是所需的
- 主题
- 第
- File1中
- 文件2
我该如何解决这个问题? Docutils文档是......不适当的,特别是在控制截面深度方面。我尝试过的一个显而易见的事情是返回doc_section.children
而不是[doc_section]
;从TOC树中完全删除File1
和File2
(但确实使文档正文中的节标题为正确的嵌套级别。)
答案 0 :(得分:1)
我不知道如何直接在自定义指令中执行此操作。但是,您可以使用自定义转换在解析后引发树中的File1
和File2
节点。例如,请参阅docutils.transforms.frontmatter
模块中的转换。
在Sphinx扩展程序中,使用Sphinx.add_transform
方法注册自定义转换。
更新:您还可以通过在节点列表中返回docutils.nodes.pending
类的一个或多个实例来直接在指令中注册转换。在这种情况下,请确保调用文档的note_pending
方法(在您的指令中,您可以通过self.state_machine.document
获取文档。)
答案 1 :(得分:1)
我认为不可能通过返回指令中的部分来做到这一点(没有按照Florian建议的那样做),因为它会被附加到“当前”部分。但是,您可以通过self.state.section
添加该部分,如下所示(为简洁起见,处理删除的选项)
class FauxHeading(object):
"""
A heading level that is not defined by a string. We need this to work with
the mechanics of
:py:meth:`docutils.parsers.rst.states.RSTState.check_subsection`.
The important thing is that the length can vary, but it must be equal to
any other instance of FauxHeading.
"""
def __init__(self, length):
self.length = length
def __len__(self):
return self.length
def __eq__(self, other):
return isinstance(other, FauxHeading)
class ParmDirective(Directive):
required_arguments = 1
optional_arguments = 0
has_content = True
option_spec = {
'type': directives.unchanged,
'precision': directives.nonnegative_int,
'scale': directives.nonnegative_int,
'length': directives.nonnegative_int}
def run(self):
variableName = self.arguments[0]
lineno = self.state_machine.abs_line_number()
secBody = None
block_length = 0
# added for some space
lineBlock = nodes.line('', '', nodes.line_block())
# parse the body of the directive
if self.has_content and len(self.content):
secBody = nodes.container()
block_length += nested_parse_with_titles(
self.state, self.content, secBody)
# keeping track of the level seems to be required if we want to allow
# nested content. Not sure why, but fits with the pattern in
# :py:meth:`docutils.parsers.rst.states.RSTState.new_subsection`
myLevel = self.state.memo.section_level
self.state.section(
variableName,
'',
FauxHeading(2 + len(self.options) + block_length),
lineno,
[lineBlock] if secBody is None else [lineBlock, secBody])
self.state.memo.section_level = myLevel
return []