The recent sphinx release has found what I think is a problem in the circular-reference detection in docutils.
We have a situation where we build a page with a directive that automatically includes a file (readme from a sub-component). In some cases, these readme's might then also include another file (e.g. similar components share a common file about their arguments, etc.). So we have a include hierarchy like
.. include:: ./component/README.rst
.. include:: common.rst
.. include:: ./component2/README.rst
.. include:: common.rst
Our directive works by reading the README.rst
file and then using self.state_machine.insert_input()
Our build started failing due a circular inclusion in "include" directive
which comes from the second inclusion of the common.rst
above. In this situation, I don't believe there is any circular inclusion -- we want the same content included twice; but it is not referencing itself.
I think it has something to do with self.state.document.include_log
thinking that it has seen common.rst
before.
I have isolated this down to the smallest example I can think of and attached that; it is also at https://paste.opendev.org/show/brTxHYllHebBIs1wHbfM/
When I run this I get
$ ./docutils-venv/bin/python3 ./recursive.py
Write combined.rst
Write file1.rst
Write file2.rst
Write common.rst
file1.rst:1: (WARNING/2) circular inclusion in "include" directive:
file1.rst
> file1.rst
Thank you for reporting a problem.
It seems related to the changes required to fix [bugs:#366] in [r8851].
With the stock
include
directive, Docutils correctly identifies "common.rst" as included from different files and does not throw the "circular inclusion" error.The custom directive does not add the logging information required for the detection of this case. Compare the last lines of
directives.misc.Include.run
(lines 198 ff. in docutils/parsers/rst/directives/misc.py).Related
Bugs:
#366Commit: [r8851]
Thanks -- I guess the problem here is that this directive uses
insert_input
to insert input which then just happens to have a.. include::
directive inside it.It seems like the suggestion is to directly modify the
document.include_log
which is probably close to the change I've suggested in our actual code athttps://review.opendev.org/c/zuul/zuul-sphinx/+/862215/2/zuul_sphinx/zuul.py
It feels like a bit of a breaking change to
insert_input
if the include log needs to be manually managed around that call?insert_input()
is an undocumented (internal) auxiliary method.Maybe the custom directive can subclass or call
directives.misc.Include
instead.Just emptying the document's
include_log
breaks the detection of circular inclusions :((You will only recognise this in CI, if there are test cases that have both, the custom directive and a circular inclusion.)
Checking the test example file, I realized that it reports:
i.e. the inclusion detection assumes that "file1.rst" includes itself recursively!
This is due to a wrong usage of
state_machine.insert_input()
:the line written by
DirectiveThatIncludes.run()
will (when parsed) include the source "name", but it is not from source "name". Withthere is no warning (because, the inclusion of "file1" and "file2" uses the stock
misc.Directive
class that handles circular inclusion detection fine).Even when using
the circular-inclusion warning does not appear here.
Thank you for your time looking at this. I'm still a bit confused as to what is going on, as we do try in the plugin to set the name/source field correctly when using
insert_input
.In the above, I'm not sure the
lines = f.read()
quite works because I don't think that puts it into a nice array of lines for parsing; so I think it might hide the issue.I have been back to our build environment and found some more details -- including bisecting this down to a change between sphinx 5.2.3 and 5.3; see https://github.com/sphinx-doc/sphinx/issues/10951
Additionally, this only seems to occur with docutils 0.17 -- if I downgrade to 0.16 or upgrade to 0.18 I don't the same build error! However we remain on 0.17 due to a dependency from
rtd_sphinx_theme
The
lines = f.read()
in my small example is a stub.The point is, that if you insist on
statemachine.insert_input()
to inject generated lines to the document while parsing it, either use the currently active source or a "fancy description" as second argument, e.g.This works in 0.18 but I can't tell for 0.17.
This is very important information, because [bugs:#366] was reported for 0.17 and fixed in 0.18.
The bug report described cases of "false positives" in the circular inclusion detection, reproducible in stock Docutils for specific input cases. Maybe your problem is triggered by the same fault to the 0.17 version of the detection algorithm.
The fix includes incompatible changes to the "manual management" of the
include_log
required around aninsert_input
call. This means any fix on your side should be tested across Docutils versions :(Related
Bugs:
#366Thanks for reporting.
Closing as "out-of-date", as we are working on 0.20 by now.