Title: bug: attributes using dict comprehension are incorrectly displayed · Issue #311 · mkdocstrings/python · GitHub
Open Graph Title: bug: attributes using dict comprehension are incorrectly displayed · Issue #311 · mkdocstrings/python
X Title: bug: attributes using dict comprehension are incorrectly displayed · Issue #311 · mkdocstrings/python
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...
Open Graph 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 char...
X 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 char...
Opengraph URL: https://github.com/mkdocstrings/python/issues/311
X: @github
Domain: patch-diff.githubusercontent.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"bug: attributes using dict comprehension are incorrectly displayed","articleBody":"### Description of the bug\n\u003c!-- Please provide a clear and concise description of what the bug is. --\u003e\nWhen displaying (with some options detailed later) a module attribute initialized with dictionary comprehension, the documentation shows incorrect Python code and random characters.\n\nFor example,\n\n```python\nTEST_0_INDEX_BY_KEY = {obj.key: obj for obj in []}\n```\n\nIs displayed in the documentation generated by `mkdocs` as:\n\n```python\nTEST_0_INDEX_BY_KEY = {(key): _Hlfor obj in []}\n```\n\nThe options I used to trigger this behaviour were `separate_signature: true` and `signature_crossrefs: true`.\n\n### To Reproduce\n\u003c!-- Please provide a Minimal Reproducible Example (MRE) if possible.\n Try to boil down the problem to a few lines of code.\n Your code should run by simply copying and pasting it.\n\n Example:\n\n ```\n git clone https://github.com/username/repro\n cd repro\n python -m venv .venv\n . .venv/bin/activate\n pip install -r requirements.txt\n ... # command or code showing the issue\n ```\n--\u003e\nI reproduced using 3 files:\n\n- `test_dict.py`\n\n```python\nTEST_0_INDEX_BY_KEY = {obj.key: obj for obj in []}\n\"\"\"Showing `(key): \u003crandom characters\u003e` instead of `obj.key: obj`.\"\"\"\n\nTEST_1_ACCESS_MEMBER = {obj: obj.member for obj in []}\n\"\"\"Showing `obj: (member)` instead of `obj: obj.member`.\"\"\"\n\nTEST_2_LONG_NAME = {\n some_object_with_a_long_name: some_object_with_a_long_name\n for some_object_with_a_long_name in []\n}\n\"\"\"Showing `some_object_with_a_long_name: \u003cmore random characters\u003e` instead of `some_object_with_a_long_name: some_object_with_a_long_name`.\"\"\"\n```\n\n- `mkdocs.yml`\n\n```yaml\nsite_name: Test Python dict comprehension\n\nplugins:\n - mkdocstrings:\n handlers:\n python:\n options:\n separate_signature: true\n signature_crossrefs: true\n```\n\n- `docs/index.md`\n\n```markdown\n::: test_dict\n```\n\nThe 3 files can be created with this command (which uses a base64-encoded tar.gz archive):\n\n```sh\necho 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\n\n```\n\nThen, install `mkdocs` and `mkdocstrings-python` and launch the server:\n\n```sh\npip install mkdocs mkdocstrings-python\nmkdocs serve\n```\n\nhttp://127.0.0.1:8000/ shows:\n\n\u003cimg width=\"1325\" height=\"626\" alt=\"Image\" src=\"https://github.com/user-attachments/assets/d4c2a4bb-975b-4043-aa42-bf9cbca81f10\" /\u003e\n\nThe 3 Python snippets are:\n\n```python\nTEST_0_INDEX_BY_KEY = {(key): _Z5for obj in []}\n\nTEST_1_ACCESS_MEMBER = {obj: (member)for obj in []}\n\nTEST_2_LONG_NAME = {some_object_with_a_long_name: _6Jpv7OmLiQU4bm2xsgNeOyqDsdmfor some_object_with_a_long_name in []}\n```\n\nIt is also possible to run `mkdocs build` and read `site/index.html`:\n\n\u003cdetails\u003e\n \u003csummary\u003eExtract of \u003ccode\u003esite/index.html\u003c/code\u003e\u003c/summary\u003e\n\n```html\n\u003ch2 id=\"test_dict.TEST_0_INDEX_BY_KEY\" class=\"doc doc-heading\"\u003e\n \u003cspan class=\"doc doc-object-name doc-attribute-name\"\u003eTEST_0_INDEX_BY_KEY\u003c/span\u003e\n\n\n \u003cspan class=\"doc doc-labels\"\u003e\n \u003csmall class=\"doc doc-label doc-label-module-attribute\"\u003e\u003ccode\u003emodule-attribute\u003c/code\u003e\u003c/small\u003e\n \u003c/span\u003e\n\n\u003c/h2\u003e\n\u003cpre class=\"highlight\"\u003e\u003ccode class=\"language-python doc-signature\"\u003eTEST_0_INDEX_BY_KEY = {(\u003cspan title=\"obj.key\"\u003ekey\u003c/span\u003e): _xEfor \u003cspan title=\"obj\"\u003eobj\u003c/span\u003e in []}\u003c/code\u003e\u003c/pre\u003e\n\n \u003cdiv class=\"doc doc-contents \"\u003e\n\n \u003cp\u003eShowing \u003ccode\u003e(key): \u0026lt;random characters\u0026gt;\u003c/code\u003e instead of \u003ccode\u003eobj.key: obj\u003c/code\u003e.\u003c/p\u003e\n\n \u003c/div\u003e\n\n\u003c/div\u003e\n\n\u003cdiv class=\"doc doc-object doc-attribute\"\u003e\n\n\n\n\u003ch2 id=\"test_dict.TEST_1_ACCESS_MEMBER\" class=\"doc doc-heading\"\u003e\n \u003cspan class=\"doc doc-object-name doc-attribute-name\"\u003eTEST_1_ACCESS_MEMBER\u003c/span\u003e\n\n\n \u003cspan class=\"doc doc-labels\"\u003e\n \u003csmall class=\"doc doc-label doc-label-module-attribute\"\u003e\u003ccode\u003emodule-attribute\u003c/code\u003e\u003c/small\u003e\n \u003c/span\u003e\n\n\u003c/h2\u003e\n\u003cpre class=\"highlight\"\u003e\u003ccode class=\"language-python doc-signature\"\u003eTEST_1_ACCESS_MEMBER = {\u003cspan title=\"obj\"\u003eobj\u003c/span\u003e: (\u003cspan title=\"obj.member\"\u003emember\u003c/span\u003e)for \u003cspan title=\"obj\"\u003eobj\u003c/span\u003e in []}\u003c/code\u003e\u003c/pre\u003e\n\n \u003cdiv class=\"doc doc-contents \"\u003e\n\n \u003cp\u003eShowing \u003ccode\u003eobj: (member)\u003c/code\u003e instead of \u003ccode\u003eobj: obj.member\u003c/code\u003e.\u003c/p\u003e\n\n \u003c/div\u003e\n\n\u003c/div\u003e\n\n\u003cdiv class=\"doc doc-object doc-attribute\"\u003e\n\n\n\n\u003ch2 id=\"test_dict.TEST_2_LONG_NAME\" class=\"doc doc-heading\"\u003e\n \u003cspan class=\"doc doc-object-name doc-attribute-name\"\u003eTEST_2_LONG_NAME\u003c/span\u003e\n\n\n \u003cspan class=\"doc doc-labels\"\u003e\n \u003csmall class=\"doc doc-label doc-label-module-attribute\"\u003e\u003ccode\u003emodule-attribute\u003c/code\u003e\u003c/small\u003e\n \u003c/span\u003e\n\n\u003c/h2\u003e\n\u003cpre class=\"highlight\"\u003e\u003ccode class=\"language-python doc-signature\"\u003eTEST_2_LONG_NAME = {\u003cspan title=\"some_object_with_a_long_name\"\u003esome_object_with_a_long_name\u003c/span\u003e: _7kTCpbiedqLcznZoOnrjKhe3D9Gfor \u003cspan title=\"some_object_with_a_long_name\"\u003esome_object_with_a_long_name\u003c/span\u003e in []}\u003c/code\u003e\u003c/pre\u003e\n\n \u003cdiv class=\"doc doc-contents \"\u003e\n\n \u003cp\u003eShowing \u003ccode\u003esome_object_with_a_long_name: \u0026lt;more random characters\u0026gt;\u003c/code\u003e instead of \u003ccode\u003esome_object_with_a_long_name: some_object_with_a_long_name\u003c/code\u003e.\u003c/p\u003e\n\n \u003c/div\u003e\n\n\u003c/div\u003e\n```\n\u003c/details\u003e\nWithout `separate_signature: true`, this generates code which is mostly right (a space is missing before `for`, and some extra parentheses are added):\n\n```python\nTEST_0_INDEX_BY_KEY = {(obj.key): objfor obj in []}\n\nTEST_1_ACCESS_MEMBER = {obj: (obj.member)for obj in []}\n\nTEST_2_LONG_NAME = {some_object_with_a_long_name: some_object_with_a_long_namefor some_object_with_a_long_name in []}\n```\n\nWith `separate_signature: true` and without `signature_crossrefs: true`, `obj.` disappears from the first two reproducers:\n\n```python\nTEST_0_INDEX_BY_KEY = {(key): objfor obj in []}\n\nTEST_1_ACCESS_MEMBER = {obj: (member)for obj in []}\n\nTEST_2_LONG_NAME = {some_object_with_a_long_name: some_object_with_a_long_namefor some_object_with_a_long_name in []}\n```\n\n\n### Expected behavior\n\u003c!-- Please provide a clear and concise description of what you expected to happen. --\u003e\nI expect the Python code displayed by mkdocs to be consistent.\n\n### Environment information\n\u003c!-- Please run the following command in your repository and paste its output below it,\n redacting sensitive information. --\u003e\n\nI tested in a Python 3.13 container with the current git `main` version:\n\n```bash\npodman run --rm --net=host -it docker.io/library/python:3.13 bash\n\npip install 'git+https://github.com/mkdocs/mkdocs'\npip install 'git+https://github.com/mkdocstrings/python'\n```\n\nThis installed:\n\n```console\n$ pip freeze\nclick==8.3.0\ncolorama==0.4.6\nghp-import==2.1.0\ngriffe==1.14.0\nJinja2==3.1.6\nMarkdown==3.9\nMarkupSafe==3.0.3\nmergedeep==1.3.4\nmkdocs @ git+https://github.com/mkdocs/mkdocs@f68e5fc18d3cd3811dbddfdeda1a57721f9e2e4e\nmkdocs-autorefs==1.4.3\nmkdocs-get-deps==0.2.0\nmkdocstrings==0.30.1\nmkdocstrings-python @ git+https://github.com/mkdocstrings/python@f25b2ecf73f30ac0ea836bec1aa60e87a360e23a\npackaging==25.0\npathspec==0.12.1\nplatformdirs==4.4.0\npymdown-extensions==10.16.1\npython-dateutil==2.9.0.post0\nPyYAML==6.0.3\npyyaml_env_tag==1.1\nsix==1.17.0\nwatchdog==6.0.0\n```\n\n```bash\npython -m mkdocstrings_handlers.python._internal.debug # | xclip -selection clipboard\n```\n\n- __System__: Linux-6.8.0-85-generic-x86_64-with-glibc2.39\n- __Python__: cpython 3.13.0 (/usr/local/bin/python)\n- __Environment variables__:\n- __Installed packages__:\n - `mkdocstrings-python` v1.18.3.dev1+gf25b2ec\n\nI also tested with [`uv`](https://docs.astral.sh/uv/) and several Python versions:\n\n```bash\nuv run --python python3.13 --with mkdocs --with mkdocstrings-python mkdocs serve\n\nuv run --python python3.14 --with mkdocs --with mkdocstrings-python mkdocs serve\n```\n\n### Additional context\n\u003c!-- Add any other relevant context about the problem here,\n like links to other issues or pull requests, screenshots, etc.\n--\u003e\nI 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:\n\n```python\nCLASS_BY_NAME = {cls.name: cls for cls in ALL_CLASSES}\n```\n\nThe 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 []}`).","author":{"url":"https://github.com/niooss-ledger","@type":"Person","name":"niooss-ledger"},"datePublished":"2025-10-08T15:02:00.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":2},"url":"https://github.com/311/python/issues/311"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:12239d3b-8028-7ef6-0ba8-1bb8d4532407 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | AE78:283961:8B246CF:B983EF7:696E185E |
| html-safe-nonce | de2b9c5e216545c483a741ae3b14653d6350b9c97b6cb0d35f2280a1aebd25ba |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJBRTc4OjI4Mzk2MTo4QjI0NkNGOkI5ODNFRjc6Njk2RTE4NUUiLCJ2aXNpdG9yX2lkIjoiMzc5NjcyODY5MzE3ODM3NDIzOCIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9 |
| visitor-hmac | 06ced3da3573a7c3e85ddf5f2f0884f76e797a98cb5683942cabfd2fa80e4c62 |
| hovercard-subject-tag | issue:3495826324 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/mkdocstrings/python/311/issue_layout |
| twitter:image | https://opengraph.githubassets.com/dac62ecc85acda496273a7ba32db72b519d6f31f36f6305d6512d1ec73021d91/mkdocstrings/python/issues/311 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/dac62ecc85acda496273a7ba32db72b519d6f31f36f6305d6512d1ec73021d91/mkdocstrings/python/issues/311 |
| og:image:alt | 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 char... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | niooss-ledger |
| hostname | github.com |
| expected-hostname | github.com |
| None | 113cc336e7105fbb9f2ee8beb67c6cc584d65fa66a8a1a5dd236ba66db174af7 |
| turbo-cache-control | no-preview |
| go-import | github.com/mkdocstrings/python git https://github.com/mkdocstrings/python.git |
| octolytics-dimension-user_id | 75664361 |
| octolytics-dimension-user_login | mkdocstrings |
| octolytics-dimension-repository_id | 422878404 |
| octolytics-dimension-repository_nwo | mkdocstrings/python |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 422878404 |
| octolytics-dimension-repository_network_root_nwo | mkdocstrings/python |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 1d23f8b0bc31294c6a73ba2f3adfdd8c7052ca1e |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width