mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Add an `'include-read'
` event (#11657)
This commit is contained in:
parent
74329d9c51
commit
ff18318613
6
CHANGES
6
CHANGES
@ -28,6 +28,10 @@ Bugs fixed
|
||||
when an object claims to be an instance of ``type``,
|
||||
but is not a class.
|
||||
Patch by James Braza.
|
||||
* 11620: Cease emitting :event:`source-read` events for files read via
|
||||
the :dudir:`include` directive.
|
||||
* 11620: Add a new :event:`include-read` for observing and transforming
|
||||
the content of included files via the :dudir:`include` directive.
|
||||
|
||||
Testing
|
||||
-------
|
||||
@ -143,7 +147,7 @@ Features added
|
||||
* #11572: Improve ``debug`` logging of reasons why files are detected as out of
|
||||
date.
|
||||
Patch by Eric Larson.
|
||||
* #10678: Emit "source-read" events for files read via
|
||||
* #10678: Emit :event:`source-read` events for files read via
|
||||
the :dudir:`include` directive.
|
||||
Patch by Halldor Fannar.
|
||||
* #11570: Use short names when using :pep:`585` built-in generics.
|
||||
|
@ -260,6 +260,23 @@ Here is a more detailed list of these events.
|
||||
|
||||
.. versionadded:: 0.5
|
||||
|
||||
.. event:: include-read (app, relative_path, parent_docname, content)
|
||||
|
||||
Emitted when a file has been read with the :dudir:`include` directive.
|
||||
The *relative_path* argument is a :py:class:`~pathlib.Path` object representing
|
||||
the relative path of the included file from the :term:`source directory`.
|
||||
The *parent_docname* argument is the name of the document that
|
||||
contains the :dudir:`include` directive.
|
||||
The *source* argument is a list whose single element is
|
||||
the contents of the included file.
|
||||
You can process the contents and replace this item
|
||||
to transform the included content,
|
||||
as with the :event:`source-read` event.
|
||||
|
||||
.. versionadded:: 7.2.5
|
||||
|
||||
.. seealso:: The :dudir:`include` directive and the :event:`source-read` event.
|
||||
|
||||
.. event:: object-description-transform (app, domain, objtype, contentnode)
|
||||
|
||||
Emitted when an object description directive has run. The *domain* and
|
||||
|
@ -1,7 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from os.path import abspath
|
||||
from os.path import abspath, relpath
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
|
||||
from docutils import nodes
|
||||
@ -19,7 +20,6 @@ from sphinx.util import docname_join, logging, url_re
|
||||
from sphinx.util.docutils import SphinxDirective
|
||||
from sphinx.util.matching import Matcher, patfilter
|
||||
from sphinx.util.nodes import explicit_title_re
|
||||
from sphinx.util.osutil import os_path
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from docutils.nodes import Element, Node
|
||||
@ -373,24 +373,25 @@ class Include(BaseInclude, SphinxDirective):
|
||||
|
||||
def run(self) -> list[Node]:
|
||||
|
||||
# To properly emit "source-read" events from included RST text,
|
||||
# To properly emit "include-read" events from included RST text,
|
||||
# we must patch the ``StateMachine.insert_input()`` method.
|
||||
# In the future, docutils will hopefully offer a way for Sphinx
|
||||
# to provide the RST parser to use
|
||||
# when parsing RST text that comes in via Include directive.
|
||||
def _insert_input(include_lines, source):
|
||||
# First, we need to combine the lines back into text so that
|
||||
# we can send it with the source-read event.
|
||||
# we can send it with the include-read event.
|
||||
# In docutils 0.18 and later, there are two lines at the end
|
||||
# that act as markers.
|
||||
# We must preserve them and leave them out of the source-read event:
|
||||
# We must preserve them and leave them out of the include-read event:
|
||||
text = "\n".join(include_lines[:-2])
|
||||
|
||||
# The docname to pass into the source-read event
|
||||
docname = self.env.path2doc(abspath(os_path(source)))
|
||||
# Emit the "source-read" event
|
||||
path = Path(relpath(abspath(source), start=self.env.srcdir))
|
||||
docname = self.env.docname
|
||||
|
||||
# Emit the "include-read" event
|
||||
arg = [text]
|
||||
self.env.app.events.emit("source-read", docname, arg)
|
||||
self.env.app.events.emit('include-read', path, docname, arg)
|
||||
text = arg[0]
|
||||
|
||||
# Split back into lines and reattach the two marker lines
|
||||
@ -401,8 +402,8 @@ class Include(BaseInclude, SphinxDirective):
|
||||
# the *Instance* method and this call is to the *Class* method.
|
||||
return StateMachine.insert_input(self.state_machine, include_lines, source)
|
||||
|
||||
# Only enable this patch if there are listeners for 'source-read'.
|
||||
if self.env.app.events.listeners.get('source-read'):
|
||||
# Only enable this patch if there are listeners for 'include-read'.
|
||||
if self.env.app.events.listeners.get('include-read'):
|
||||
# See https://github.com/python/mypy/issues/2427 for details on the mypy issue
|
||||
self.state_machine.insert_input = _insert_input # type: ignore[method-assign]
|
||||
|
||||
|
@ -38,6 +38,7 @@ core_events = {
|
||||
'env-before-read-docs': 'env, docnames',
|
||||
'env-check-consistency': 'env',
|
||||
'source-read': 'docname, source text',
|
||||
'include-read': 'relative path, parent docname, source text',
|
||||
'doctree-read': 'the doctree before being pickled',
|
||||
'env-merge-info': 'env, read docnames, other env instance',
|
||||
'missing-reference': 'env, node, contnode',
|
||||
|
1
tests/roots/test-directive-include/bar.txt
Normal file
1
tests/roots/test-directive-include/bar.txt
Normal file
@ -0,0 +1 @@
|
||||
Text from :file:`bar.txt`.
|
@ -3,4 +3,4 @@ Baz
|
||||
|
||||
.. include:: foo.rst
|
||||
|
||||
Baz was here.
|
||||
Baz was here.
|
||||
|
@ -1,4 +1,5 @@
|
||||
"""Test the other directives."""
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from docutils import nodes
|
||||
@ -151,34 +152,41 @@ def test_toctree_twice(app):
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='directive-include')
|
||||
def test_include_source_read_event(app):
|
||||
sources_reported = {}
|
||||
def test_include_include_read_event(app):
|
||||
sources_reported = []
|
||||
|
||||
def source_read_handler(app, doc, source):
|
||||
sources_reported[doc] = source[0]
|
||||
def source_read_handler(_app, relative_path, parent_docname, source):
|
||||
sources_reported.append((relative_path, parent_docname, source[0]))
|
||||
|
||||
app.connect("source-read", source_read_handler)
|
||||
text = (".. include:: baz/baz.rst\n"
|
||||
" :start-line: 4\n\n"
|
||||
".. include:: text.txt\n"
|
||||
" :literal: \n")
|
||||
app.connect("include-read", source_read_handler)
|
||||
text = """\
|
||||
.. include:: baz/baz.rst
|
||||
:start-line: 4
|
||||
.. include:: text.txt
|
||||
:literal:
|
||||
.. include:: bar.txt
|
||||
"""
|
||||
app.env.find_files(app.config, app.builder)
|
||||
restructuredtext.parse(app, text, 'index')
|
||||
assert "index" in sources_reported
|
||||
assert "text.txt" not in sources_reported # text was included as literal, no rst parsing
|
||||
assert "baz/baz" in sources_reported
|
||||
assert sources_reported["baz/baz"] == "\nBaz was here."
|
||||
|
||||
included_files = {filename.as_posix()
|
||||
for filename, p, s in sources_reported}
|
||||
assert 'index.rst' not in included_files # sources don't emit 'include-read'
|
||||
assert 'baz/baz.rst' in included_files
|
||||
assert 'text.txt' not in included_files # text was included as literal, no rst parsing
|
||||
assert 'bar.txt' in included_files # suffix not in source-suffixes
|
||||
assert (Path('baz/baz.rst'), 'index', '\nBaz was here.') in sources_reported
|
||||
|
||||
|
||||
@pytest.mark.sphinx(testroot='directive-include')
|
||||
def test_include_source_read_event_nested_includes(app):
|
||||
def test_include_include_read_event_nested_includes(app):
|
||||
|
||||
def source_read_handler(app, doc, source):
|
||||
def source_read_handler(_app, _relative_path, _parent_docname, source):
|
||||
text = source[0].replace("#magical", "amazing")
|
||||
source[0] = text
|
||||
|
||||
app.connect("source-read", source_read_handler)
|
||||
text = (".. include:: baz/baz.rst\n")
|
||||
app.connect("include-read", source_read_handler)
|
||||
text = ".. include:: baz/baz.rst\n"
|
||||
app.env.find_files(app.config, app.builder)
|
||||
doctree = restructuredtext.parse(app, text, 'index')
|
||||
assert_node(doctree, addnodes.document)
|
||||
|
Loading…
Reference in New Issue
Block a user