-
-
Notifications
You must be signed in to change notification settings - Fork 49
Description
Description of the bug
When displaying (with some options detailed later) a module attribute initialized with dictionary comprehension, the documentation shows incorrect Python code and random characters.
For example,
TEST_0_INDEX_BY_KEY = {obj.key: obj for obj in []}
Is displayed in the documentation generated by mkdocs
as:
TEST_0_INDEX_BY_KEY = {(key): _Hlfor obj in []}
The options I used to trigger this behaviour were separate_signature: true
and signature_crossrefs: true
.
To Reproduce
I reproduced using 3 files:
test_dict.py
TEST_0_INDEX_BY_KEY = {obj.key: obj for obj in []}
"""Showing `(key): <random characters>` instead of `obj.key: obj`."""
TEST_1_ACCESS_MEMBER = {obj: obj.member for obj in []}
"""Showing `obj: (member)` instead of `obj: obj.member`."""
TEST_2_LONG_NAME = {
some_object_with_a_long_name: some_object_with_a_long_name
for some_object_with_a_long_name in []
}
"""Showing `some_object_with_a_long_name: <more random characters>` instead of `some_object_with_a_long_name: some_object_with_a_long_name`."""
mkdocs.yml
site_name: Test Python dict comprehension
plugins:
- mkdocstrings:
handlers:
python:
options:
separate_signature: true
signature_crossrefs: true
docs/index.md
::: test_dict
The 3 files can be created with this command (which uses a base64-encoded tar.gz archive):
echo H4sIAAAAAAACA+3WUWvbMBAAYD/7Vxx5ah/myYljg+kGbWfG2JKNJQ8rYyiurcReI8tICl0Y++87OzFLN3Cg0IzCfS+OrYvucO5QrDCW52VmvXrrPBGGwjBor+jvK4uiyPHHLPKDMAiZ7zB/6I98B5hzAhtjUw3gaKVsX9yx9WdqnszmnPF30zfJF351w98nN/AKfqrb796d2MaAH2CpdHstK/j67Zc7GAxmhbovqxUszjDoPIYLnVa5kpAVqU4zK7R5vcBwY0Wag1rC4nC/hYc7uG6b2eeX19fJbMYnyeQq+bxP3YZ5Ushbofuyt6Fnu7jzfxIe7nKYc8g/fJy+5dPLSdLkcwEZJQXHcJFZfl/agqd8raoVr1Ip4t7V9utNjX1Bu+Ldh9X357yQSgs49l4fX/fujTjyLleZ8bZy/XQ9dmz+Ryzo5n8cDUc4//44YjT/p2BKK/bdMsejAD5tbaEqaE4EyJSstShEZUpVuW693qyw92Ls+Bew6xursZPbJ40Cm3WNPdrdA9Ttbn/uAVRtcTNz+AinT9TY31iIKVdVajcaq7F6Ix4GdWs808oYLZZmH+WQR2t+xZdllYsfnsz/0/wzP+zmfzgehe35H4xp/k8hjnGIuv+ANEmEEEIIIYQQQgghhBBCCCGEEPJs/QaXqtegACgAAA== | base64 -d | tar -xz
Then, install mkdocs
and mkdocstrings-python
and launch the server:
pip install mkdocs mkdocstrings-python
mkdocs serve
http://127.0.0.1:8000/ shows:

The 3 Python snippets are:
TEST_0_INDEX_BY_KEY = {(key): _Z5for obj in []}
TEST_1_ACCESS_MEMBER = {obj: (member)for obj in []}
TEST_2_LONG_NAME = {some_object_with_a_long_name: _6Jpv7OmLiQU4bm2xsgNeOyqDsdmfor some_object_with_a_long_name in []}
It is also possible to run mkdocs build
and read site/index.html
:
Extract of site/index.html
<h2 id="test_dict.TEST_0_INDEX_BY_KEY" class="doc doc-heading">
<span class="doc doc-object-name doc-attribute-name">TEST_0_INDEX_BY_KEY</span>
<span class="doc doc-labels">
<small class="doc doc-label doc-label-module-attribute"><code>module-attribute</code></small>
</span>
</h2>
<pre class="highlight"><code class="language-python doc-signature">TEST_0_INDEX_BY_KEY = {(<span title="obj.key">key</span>): _xEfor <span title="obj">obj</span> in []}</code></pre>
<div class="doc doc-contents ">
<p>Showing <code>(key): <random characters></code> instead of <code>obj.key: obj</code>.</p>
</div>
</div>
<div class="doc doc-object doc-attribute">
<h2 id="test_dict.TEST_1_ACCESS_MEMBER" class="doc doc-heading">
<span class="doc doc-object-name doc-attribute-name">TEST_1_ACCESS_MEMBER</span>
<span class="doc doc-labels">
<small class="doc doc-label doc-label-module-attribute"><code>module-attribute</code></small>
</span>
</h2>
<pre class="highlight"><code class="language-python doc-signature">TEST_1_ACCESS_MEMBER = {<span title="obj">obj</span>: (<span title="obj.member">member</span>)for <span title="obj">obj</span> in []}</code></pre>
<div class="doc doc-contents ">
<p>Showing <code>obj: (member)</code> instead of <code>obj: obj.member</code>.</p>
</div>
</div>
<div class="doc doc-object doc-attribute">
<h2 id="test_dict.TEST_2_LONG_NAME" class="doc doc-heading">
<span class="doc doc-object-name doc-attribute-name">TEST_2_LONG_NAME</span>
<span class="doc doc-labels">
<small class="doc doc-label doc-label-module-attribute"><code>module-attribute</code></small>
</span>
</h2>
<pre class="highlight"><code class="language-python doc-signature">TEST_2_LONG_NAME = {<span title="some_object_with_a_long_name">some_object_with_a_long_name</span>: _7kTCpbiedqLcznZoOnrjKhe3D9Gfor <span title="some_object_with_a_long_name">some_object_with_a_long_name</span> in []}</code></pre>
<div class="doc doc-contents ">
<p>Showing <code>some_object_with_a_long_name: <more random characters></code> instead of <code>some_object_with_a_long_name: some_object_with_a_long_name</code>.</p>
</div>
</div>
TEST_0_INDEX_BY_KEY = {(obj.key): objfor obj in []}
TEST_1_ACCESS_MEMBER = {obj: (obj.member)for obj in []}
TEST_2_LONG_NAME = {some_object_with_a_long_name: some_object_with_a_long_namefor some_object_with_a_long_name in []}
With separate_signature: true
and without signature_crossrefs: true
, obj.
disappears from the first two reproducers:
TEST_0_INDEX_BY_KEY = {(key): objfor obj in []}
TEST_1_ACCESS_MEMBER = {obj: (member)for obj in []}
TEST_2_LONG_NAME = {some_object_with_a_long_name: some_object_with_a_long_namefor some_object_with_a_long_name in []}
Expected behavior
I expect the Python code displayed by mkdocs to be consistent.
Environment information
I tested in a Python 3.13 container with the current git main
version:
podman run --rm --net=host -it docker.io/library/python:3.13 bash
pip install 'git+https://github.com/mkdocs/mkdocs'
pip install 'git+https://github.com/mkdocstrings/python'
This installed:
$ pip freeze
click==8.3.0
colorama==0.4.6
ghp-import==2.1.0
griffe==1.14.0
Jinja2==3.1.6
Markdown==3.9
MarkupSafe==3.0.3
mergedeep==1.3.4
mkdocs @ git+https://github.com/mkdocs/mkdocs@f68e5fc18d3cd3811dbddfdeda1a57721f9e2e4e
mkdocs-autorefs==1.4.3
mkdocs-get-deps==0.2.0
mkdocstrings==0.30.1
mkdocstrings-python @ git+https://github.com/mkdocstrings/python@f25b2ecf73f30ac0ea836bec1aa60e87a360e23a
packaging==25.0
pathspec==0.12.1
platformdirs==4.4.0
pymdown-extensions==10.16.1
python-dateutil==2.9.0.post0
PyYAML==6.0.3
pyyaml_env_tag==1.1
six==1.17.0
watchdog==6.0.0
python -m mkdocstrings_handlers.python._internal.debug # | xclip -selection clipboard
- System: Linux-6.8.0-85-generic-x86_64-with-glibc2.39
- Python: cpython 3.13.0 (/usr/local/bin/python)
- Environment variables:
- Installed packages:
mkdocstrings-python
v1.18.3.dev1+gf25b2ec
I also tested with uv
and several Python versions:
uv run --python python3.13 --with mkdocs --with mkdocstrings-python mkdocs serve
uv run --python python3.14 --with mkdocs --with mkdocstrings-python mkdocs serve
Additional context
I encountered this issue in a project where several classes are defined (ClsA
, ClsB
, ClsC
), a global variable listes the classes (ALL_CLASSES = (ClsA, ClsB, ClsC)
) and another global variable enables getting a class through their name:
CLASS_BY_NAME = {cls.name: cls for cls in ALL_CLASSES}
The documentation generated by mkdocs was buggy and this is what led to the first minimal reproducer I shared (TEST_0_INDEX_BY_KEY = {obj.key: obj for obj in []}
).