[docutils:bugs] #518 id_prefix removes footnote prefix from

Günter Milde via Docutils-develop <[email protected]> Tue, 24 Mar 2026 17:23:02 -0000
Newsgroups gmane.text.docutils.devel
Message-ID </p/docutils/bugs/518/2b2c096fe1e067a365451db0ab3b610fa990c3bb.bugs@docutils.p.sourceforge.net>
This is a multi-part message in MIME format.
--===============7151139055253967860==
Content-Type: multipart/related; boundary="===============0101529963511801710=="

This is a multi-part message in MIME format.
--===============0101529963511801710==
Content-Type: multipart/alternative; boundary="===============1166491128007995970=="
MIME-Version: 1.0

--===============1166491128007995970==
MIME-Version: 1.0
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit

Thank you for the report.
I'd classify the problem as a documentation issue rather than a bug.

The problem is the difference between an *reference name* and an *identifier* .
Identifiers are derived from reference names via the [identifier normalization](https://docutils.sourceforge.io/docs/ref/rst/directives.html#identifier-normalization). Due to identifier restriction in HTML4, a reference name consisting of only digits "vanishes completely" unless prefixed. (cf. https://sourceforge.net/p/docutils/feature-requests/66/).

This is why the reference-name "5" is replaced by the auto-generated ID "footnote-1" (with 1 just be a running number to disambiguate all "footnote" IDs). A similar problem happens to, e.g. section headings consiting of just a date.
However, prefixing the ID with "user-content" allows the "1" to be appended.

You may play with the attached test script.


Attachments:

- [ids.py](https://sourceforge.net/p/docutils/bugs/_discuss/thread/8a618f0801/b4e5/attachment/ids.py) (832 Bytes; text/x-python)


---

**[bugs:#518] id_prefix removes footnote prefix from **

**Status:** open
**Created:** Sun Mar 22, 2026 12:40 AM UTC by miketheman
**Last Updated:** Sun Mar 22, 2026 12:40 AM UTC
**Owner:** nobody


I was recently working on adding an `id_prefix` string to enable the behavior as described in https://docutils.sourceforge.io/docs/user/config.html#id-prefix

It works pretty consistently, however when I came across a footnote, it removed the `footnote-` prefix from the rendered ID, but not the associated `href` value, leaving the rendered links a little more confusing than before.

Here's a reproduction example:

```python
import sys
from difflib import unified_diff

from docutils.core import publish_parts


INPUT_RST_WITH_FOOTNOTES = """
Footnote reference, like [5]_.

Some Text

.. [5] A numerical footnote.
"""


def render_body(rst: str, settings: dict) -> str:
    return publish_parts(rst, writer="html5", settings_overrides=settings)["body"]


settings = {"output_encoding": "unicode"}
basic = render_body(INPUT_RST_WITH_FOOTNOTES, settings)

settings["id_prefix"] = "user-content-"
prefixed = render_body(INPUT_RST_WITH_FOOTNOTES, settings)

sys.stdout.writelines(
    unified_diff(
        basic.splitlines(keepends=True),
        prefixed.splitlines(keepends=True),
        fromfile="basic.html",
        tofile="prefixed.html",
    )
)
```

Output with Docutils 0.22.4:

    :::udiff
    --- basic.html
    +++ prefixed.html
    @@ -1,8 +1,8 @@
    -<p>Footnote reference, like <a class="brackets" href="#footnote-1" id="footnote-reference-1" role="doc-noteref"><span class="fn-bracket">[</span>5<span class="fn-bracket">]</span></a>.</p>
    +<p>Footnote reference, like <a class="brackets" href="#user-content-5" id="user-content-footnote-reference-1" role="doc-noteref"><span class="fn-bracket">[</span>5<span class="fn-bracket">]</span></a>.</p>
     <p>Some Text</p>
    <aside class="footnote-list brackets">
    -<aside class="footnote brackets" id="footnote-1" role="doc-footnote">
    -<span class="label"><span class="fn-bracket">[</span><a role="doc-backlink" href="#footnote-reference-1">5</a><span class="fn-bracket">]</span></span>
    +<aside class="footnote brackets" id="user-content-5" role="doc-footnote">
    +<span class="label"><span class="fn-bracket">[</span><a role="doc-backlink" href="#user-content-footnote-reference-1">5</a><span class="fn-bracket">]</span></span>
    <p>A numerical footnote.</p>
    </aside>
    </aside>

The things that are different:

- the footnote reference changes from `1` to `5` - which is more accurate than before - yay!
- the footnote id/href value loses it's `footnote-` prefix in the reference part, the backlinks have the prefix + `footnote-`

Both link and backlink work, it's more about the inconsistent naming of the `id` and associated `href` values, and ther output behavior not matching the expectation from the documentation.

Hope this makes sense!


---

Sent from sourceforge.net because [email protected] is subscribed to https://sourceforge.net/p/docutils/bugs/

To unsubscribe from further messages, a project admin can change settings at https://sourceforge.net/p/docutils/admin/bugs/options.  Or, if this is a mailing list, you can unsubscribe from the mailing list.
--===============1166491128007995970==
MIME-Version: 1.0
Content-Type: text/html; charset="us-ascii"
Content-Transfer-Encoding: 7bit

<div class="markdown_content"><p>Thank you for the report.<br/>
I'd classify the problem as a documentation issue rather than a bug.</p>
<p>The problem is the difference between an <em>reference name</em> and an <em>identifier</em> .<br/>
Identifiers are derived from reference names via the <a href="https://docutils.sourceforge.io/docs/ref/rst/directives.html#identifier-normalization" rel="nofollow">identifier normalization</a>. Due to identifier restriction in HTML4, a reference name consisting of only digits "vanishes completely" unless prefixed. (cf. <a href="https://sourceforge.net/p/docutils/feature-requests/66/">https://sourceforge.net/p/docutils/feature-requests/66/</a>).</p>
<p>This is why the reference-name "5" is replaced by the auto-generated ID "footnote-1" (with 1 just be a running number to disambiguate all "footnote" IDs). A similar problem happens to, e.g. section headings consiting of just a date.<br/>
However, prefixing the ID with "user-content" allows the "1" to be appended.</p>
<p>You may play with the attached test script.</p>
<p>Attachments:</p>
<ul>
<li><a href="https://sourceforge.net/p/docutils/bugs/_discuss/thread/8a618f0801/b4e5/attachment/ids.py">ids.py</a> (832 Bytes; text/x-python)</li>
</ul>
<hr/>
<p>**<a class="alink" href="https://sourceforge.net/p/docutils/bugs/518/">[bugs:#518]</a> id_prefix removes footnote prefix from **</p>
<p><strong>Status:</strong> open<br/>
<strong>Created:</strong> Sun Mar 22, 2026 12:40 AM UTC by miketheman<br/>
<strong>Last Updated:</strong> Sun Mar 22, 2026 12:40 AM UTC<br/>
<strong>Owner:</strong> nobody</p>
<p>I was recently working on adding an <code>id_prefix</code> string to enable the behavior as described in <a href="https://docutils.sourceforge.io/docs/user/config.html#id-prefix" rel="nofollow">https://docutils.sourceforge.io/docs/user/config.html#id-prefix</a></p>
<p>It works pretty consistently, however when I came across a footnote, it removed the <code>footnote-</code> prefix from the rendered ID, but not the associated <code>href</code> value, leaving the rendered links a little more confusing than before.</p>
<p>Here's a reproduction example:</p>
<div class="codehilite"><pre><span></span><code><span class="kn">import</span><span class="w"> </span><span class="nn">sys</span>
<span class="kn">from</span><span class="w"> </span><span class="nn">difflib</span><span class="w"> </span><span class="kn">import</span> <span class="n">unified_diff</span>

<span class="kn">from</span><span class="w"> </span><span class="nn">docutils.core</span><span class="w"> </span><span class="kn">import</span> <span class="n">publish_parts</span>


<span class="n">INPUT_RST_WITH_FOOTNOTES</span> <span class="o">=</span> <span class="s2">"""</span>
<span class="s2">Footnote reference, like [5]_.</span>

<span class="s2">Some Text</span>

<span class="s2">.. [5] A numerical footnote.</span>
<span class="s2">"""</span>


<span class="k">def</span><span class="w"> </span><span class="nf">render_body</span><span class="p">(</span><span class="n">rst</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">settings</span><span class="p">:</span> <span class="nb">dict</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
    <span class="k">return</span> <span class="n">publish_parts</span><span class="p">(</span><span class="n">rst</span><span class="p">,</span> <span class="n">writer</span><span class="o">=</span><span class="s2">"html5"</span><span class="p">,</span> <span class="n">settings_overrides</span><span class="o">=</span><span class="n">settings</span><span class="p">)[</span><span class="s2">"body"</span><span class="p">]</span>


<span class="n">settings</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"output_encoding"</span><span class="p">:</span> <span class="s2">"unicode"</span><span class="p">}</span>
<span class="n">basic</span> <span class="o">=</span> <span class="n">render_body</span><span class="p">(</span><span class="n">INPUT_RST_WITH_FOOTNOTES</span><span class="p">,</span> <span class="n">settings</span><span class="p">)</span>

<span class="n">settings</span><span class="p">[</span><span class="s2">"id_prefix"</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"user-content-"</span>
<span class="n">prefixed</span> <span class="o">=</span> <span class="n">render_body</span><span class="p">(</span><span class="n">INPUT_RST_WITH_FOOTNOTES</span><span class="p">,</span> <span class="n">settings</span><span class="p">)</span>

<span class="n">sys</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">writelines</span><span class="p">(</span>
    <span class="n">unified_diff</span><span class="p">(</span>
        <span class="n">basic</span><span class="o">.</span><span class="n">splitlines</span><span class="p">(</span><span class="n">keepends</span><span class="o">=</span><span class="kc">True</span><span class="p">),</span>
        <span class="n">prefixed</span><span class="o">.</span><span class="n">splitlines</span><span class="p">(</span><span class="n">keepends</span><span class="o">=</span><span class="kc">True</span><span class="p">),</span>
        <span class="n">fromfile</span><span class="o">=</span><span class="s2">"basic.html"</span><span class="p">,</span>
        <span class="n">tofile</span><span class="o">=</span><span class="s2">"prefixed.html"</span><span class="p">,</span>
    <span class="p">)</span>
<span class="p">)</span>
</code></pre></div>

<p>Output with Docutils 0.22.4:</p>
<div class="codehilite"><pre><span></span><code><span class="gd">--- basic.html</span>
<span class="gi">+++ prefixed.html</span>
<span class="gu">@@ -1,8 +1,8 @@</span>
<span class="gd">-&lt;p&gt;Footnote reference, like &lt;a class="brackets" href="#footnote-1" id="footnote-reference-1" role="doc-noteref"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;5&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;</span>
<span class="gi">+&lt;p&gt;Footnote reference, like &lt;a class="brackets" href="#user-content-5" id="user-content-footnote-reference-1" role="doc-noteref"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;5&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;</span>
<span class="w"> </span>&lt;p&gt;Some Text&lt;/p&gt;
&lt;aside class="footnote-list brackets"&gt;
<span class="gd">-&lt;aside class="footnote brackets" id="footnote-1" role="doc-footnote"&gt;</span>
<span class="gd">-&lt;span class="label"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;&lt;a role="doc-backlink" href="#footnote-reference-1"&gt;5&lt;/a&gt;&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/span&gt;</span>
<span class="gi">+&lt;aside class="footnote brackets" id="user-content-5" role="doc-footnote"&gt;</span>
<span class="gi">+&lt;span class="label"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;&lt;a role="doc-backlink" href="#user-content-footnote-reference-1"&gt;5&lt;/a&gt;&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/span&gt;</span>
&lt;p&gt;A numerical footnote.&lt;/p&gt;
&lt;/aside&gt;
&lt;/aside&gt;
</code></pre></div>

<p>The things that are different:</p>
<ul>
<li>the footnote reference changes from <code>1</code> to <code>5</code> - which is more accurate than before - yay!</li>
<li>the footnote id/href value loses it's <code>footnote-</code> prefix in the reference part, the backlinks have the prefix + <code>footnote-</code></li>
</ul>
<p>Both link and backlink work, it's more about the inconsistent naming of the <code>id</code> and associated <code>href</code> values, and ther output behavior not matching the expectation from the documentation.</p>
<p>Hope this makes sense!</p>
<hr/>
<p>Sent from sourceforge.net because [email protected] is subscribed to <a href="https://sourceforge.net/p/docutils/bugs/">https://sourceforge.net/p/docutils/bugs/</a></p>
<p>To unsubscribe from further messages, a project admin can change settings at <a href="https://sourceforge.net/p/docutils/admin/bugs/options.">https://sourceforge.net/p/docutils/admin/bugs/options.</a>  Or, if this is a mailing list, you can unsubscribe from the mailing list.</p></div>
--===============1166491128007995970==--

--===============0101529963511801710==--


--===============7151139055253967860==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline


--===============7151139055253967860==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline