Title: bug: relative cross-references in inherited members are expanded against the inheriting class · Issue #341 · mkdocstrings/python · GitHub
Open Graph Title: bug: relative cross-references in inherited members are expanded against the inheriting class · Issue #341 · mkdocstrings/python
X Title: bug: relative cross-references in inherited members are expanded against the inheriting class · Issue #341 · mkdocstrings/python
Description: Description of the bug When a class inherits from a class defined in another package and inherited_members is enabled, mkdocstrings renders the base class member's docstring under the subclass. If that docstring contains a relative cross...
Open Graph Description: Description of the bug When a class inherits from a class defined in another package and inherited_members is enabled, mkdocstrings renders the base class member's docstring under the subclass. If ...
X Description: Description of the bug When a class inherits from a class defined in another package and inherited_members is enabled, mkdocstrings renders the base class member's docstring under the subclass....
Opengraph URL: https://github.com/mkdocstrings/python/issues/341
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"bug: relative cross-references in inherited members are expanded against the inheriting class","articleBody":"### Description of the bug\n\nWhen a class inherits from a class defined in another package and `inherited_members` is enabled, mkdocstrings renders the base class member's docstring under the subclass. If that docstring contains a relative cross-reference, the reference is expanded as if it had been written in the subclass, not where it was actually written.\n\nThe expansion happens in [`AutorefsHook.expand_identifier`](https://github.com/mkdocstrings/python/blob/8125b0d28b039693ff05b8caac981da47eb4cd7c/src/mkdocstrings_handlers/python/_internal/rendering.py#L805-L813), which walks `self.current_object`. For an inherited member, `current_object` is a Griffe `Alias` that lives under the inheriting class, so in the reproducer below `alias.path` is `pkgb.Derived.MAPPING` and `alias.parent` is `pkgb.Derived`, while `alias.docstring.parent` is `pkga.sub.Base.MAPPING`, the object the docstring was actually written on. Walking up N parents therefore climbs the consumer's tree instead of the dependency's.\n\nThat has two visible consequences.\n\nThe expanded identifier points at the wrong place. In the reproducer, ``[`Thing`][....Thing]`` written on `pkga.sub.Base.MAPPING` expands to `pkga.Thing` in pkga's own docs, which is correct, and to `pkgb.Thing` in the docs of a package that subclasses `Base`. When the consumer happens to have an object of that name, as `pkgb` does here, the link silently points at the unrelated object and no warning is emitted.\n\nThe expansion is also gated on the consumer's own `relative_crossrefs` setting, even though the docstring comes from a dependency. A consumer that does not enable that option gets the raw dotted string handed to autorefs:\n\n```\nWARNING - mkdocs_autorefs: index.md: from /path/to/repro/pkga/sub.py:10: (pkgb.Derived.MAPPING) Could not find cross-reference target '....Thing'\n```\n\nwhich is fatal under `strict: true`. The dependency's docstring is correct and its own documentation builds fine, but the downstream build breaks, and the only workaround on the downstream side is to turn `inherited_members` off.\n\nIncidentally, [`get_context`](https://github.com/mkdocstrings/python/blob/8125b0d28b039693ff05b8caac981da47eb4cd7c/src/mkdocstrings_handlers/python/_internal/rendering.py#L842-L848) in the same class already uses `docstring.parent.filepath`, which is why the warning above correctly points at the dependency's source file. The defining object is therefore reachable at that point.\n\n### To Reproduce\n\n```bash\nmkdir -p repro/pkga repro/pkgb repro/docs-a repro/docs-b\ncd repro\n\npython -m venv .venv\n. .venv/bin/activate\npip install -q mkdocs 'mkdocstrings[python]'\n\ncat > pkga/__init__.py \u003c\u003c'EOF'\n\"\"\"Package A (the dependency).\"\"\"\n\nfrom pkga._core import Thing\n\n__all__ = [\"Thing\"]\nEOF\n\ncat > pkga/_core.py \u003c\u003c'EOF'\n\"\"\"Core objects of package A.\"\"\"\n\n\nclass Thing:\n \"\"\"A thing, defined by package A.\"\"\"\nEOF\n\ncat > pkga/sub.py \u003c\u003c'EOF'\n\"\"\"Sub-module of package A.\"\"\"\n\nfrom typing import Any\n\n\nclass Base:\n \"\"\"A base class.\"\"\"\n\n MAPPING: dict[str, Any] = {}\n \"\"\"The mapping to use for each [`Thing`][....Thing].\"\"\"\nEOF\n\ncat > pkgb/__init__.py \u003c\u003c'EOF'\n\"\"\"Package B (the downstream consumer).\"\"\"\n\nfrom pkga.sub import Base\n\n\nclass Thing:\n \"\"\"A completely unrelated thing, which happens to have the same name.\"\"\"\n\n\nclass Derived(Base):\n \"\"\"A class derived from package A's base class.\"\"\"\nEOF\n\necho '# Package A\n\n::: pkga\n\n::: pkga.sub' > docs-a/index.md\n\necho '# Package B\n\n::: pkgb' > docs-b/index.md\n\ncat > mkdocs-a.yml \u003c\u003c'EOF'\nsite_name: pkga\ndocs_dir: docs-a\nsite_dir: site-a\nstrict: true\nplugins:\n- mkdocstrings:\n handlers:\n python:\n options:\n relative_crossrefs: true\nEOF\n\ncat > mkdocs-b.yml \u003c\u003c'EOF'\nsite_name: pkgb\ndocs_dir: docs-b\nsite_dir: site-b\nstrict: true\nplugins:\n- mkdocstrings:\n handlers:\n python:\n options:\n preload_modules: [pkga]\n inherited_members: true\n # relative_crossrefs is deliberately NOT enabled here\nEOF\n\necho \"### 1. pkga's own docs (relative_crossrefs enabled): reference is correct\"\nmkdocs build -f mkdocs-a.yml 2>&1 | tail -3\ngrep -o 'The mapping to use for each \u003ca[^>]*>' site-a/index.html | tail -1\n\necho\necho \"### 2. pkgb's docs, without relative_crossrefs: the raw dotted string leaks\"\nmkdocs build -f mkdocs-b.yml 2>&1 | tail -3 || true\ngrep -o 'The mapping to use for each .*\\.$' site-b/index.html | tail -1 || true\n\necho\necho \"### 3. pkgb's docs, with relative_crossrefs: expanded against the WRONG class\"\nsed -i 's/ # relative_crossrefs.*/ relative_crossrefs: true/' mkdocs-b.yml\nmkdocs build -f mkdocs-b.yml 2>&1 | tail -3\ngrep -o 'The mapping to use for each \u003ca[^>]*>' site-b/index.html | tail -1\n```\n\nOutput:\n\n```\n### 1. pkga's own docs (relative_crossrefs enabled): reference is correct\nINFO - Cleaning site directory\nINFO - Building documentation to directory: /tmp/repro/site-a\nINFO - Documentation built in 0.40 seconds\nThe mapping to use for each \u003ca class=\"autorefs autorefs-internal\" href=\"#pkga.Thing\">\n\n### 2. pkgb's docs, without relative_crossrefs: the raw dotted string leaks\nWARNING - mkdocs_autorefs: index.md: from /tmp/repro/pkga/sub.py:10: (pkgb.Derived.MAPPING) Could not find cross-reference target '....Thing'\n\nAborted with 1 warnings in strict mode!\n\n### 3. pkgb's docs, with relative_crossrefs: expanded against the WRONG class\nINFO - Cleaning site directory\nINFO - Building documentation to directory: /tmp/repro/site-b\nINFO - Documentation built in 0.27 seconds\nThe mapping to use for each \u003ca class=\"autorefs autorefs-internal\" href=\"#pkgb.Thing\">\n```\n\nIn the repro `pkga` sits next to `pkgb` for brevity. The real case has `pkga` installed as a dependency in `site-packages`, which changes nothing.\n\n### Full traceback\n\nThere is no traceback. The failure surfaces as the `mkdocs_autorefs` warning shown above, which aborts the build in strict mode.\n\n### Environment information\n\n```bash\npython -m mkdocstrings_handlers.python._internal.debug\n- __System__: Linux-6.12.107+deb13-amd64-x86_64-with-glibc2.42\n- __Python__: cpython 3.12.14\n- __Environment variables__:\n- __Installed packages__:\n - `mkdocs` v1.6.1\n - `mkdocstrings` v1.0.6\n - `mkdocstrings-python` v2.0.8\n - `mkdocs-autorefs` v1.4.4\n - `griffelib` v2.3.0\n```\n\n### Additional context\n\nReal-world occurrence: [frequenz-quantities](https://github.com/frequenz-floss/frequenz-quantities) documents `QuantitySchema.TYPE_MAPPING` with ``[`Quantity`][.....Quantity]``, which resolves correctly in its own documentation. [frequenz-sdk-python](https://github.com/frequenz-floss/frequenz-sdk-python) subclasses that schema and builds with `inherited_members: true` and `strict: true`, without `relative_crossrefs`, so its documentation build fails with `Could not find cross-reference target '.....Quantity'`. [Build log](https://github.com/frequenz-floss/frequenz-sdk-python/actions/runs/34208898217/job/102004915946).\n\nOn a possible direction: expanding against `self.current_object.docstring.parent` when it is set would use the object the docstring was written on. One thing to weigh is that this also changes re-export aliases and not only inherited members. For `pkga.Thing`, an alias to `pkga._core.Thing`, `docstring.parent` is `pkga._core.Thing`, so relative references in that docstring would expand against the private module rather than the alias's location. Whether the consumer's `relative_crossrefs` setting should gate the expansion of a dependency's docstring at all looks like a separate decision.\n","author":{"url":"https://github.com/llucax","@type":"Person","name":"llucax"},"datePublished":"2026-09-08T10:30:09Z","url":"https://github.com/mkdocstrings/python/issues/341"}
| react-profiling | 0 |
| react-import-map | react,react-dom,react-dom/client,react-dom/profiling,react-is,react-reconciler,react/compiler-runtime,react/jsx-dev-runtime,react/jsx-runtime,scheduler@d5750093facf |
| 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:6c1eafa4-a482-43f1-cc72-66de4d4b3c66 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | B3F8:329B1E:18BE59:221904:6AF838E7 |
| html-safe-nonce | b67e4047241d6c1051a33e92b53958835878779cb855dbd03a522a626f56b652 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJCM0Y4OjMyOUIxRToxOEJFNTk6MjIxOTA0OjZBRjgzOEU3IiwidmlzaXRvcl9pZCI6Ijc5NTYwNDk0NTAxMzg4NzYxMzUiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ== |
| visitor-hmac | 72030dc715a1191c0a6cb935d69d76feafee72b06bc908749ab241a6663b9c89 |
| hovercard-subject-tag | issue:5385540527 |
| 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/341/issue_layout |
| twitter:image | https://opengraph.githubassets.com/531fa9954bae3c76949945bce79928fa7ef6846fc273b0480a657f579021c977/mkdocstrings/python/issues/341 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/531fa9954bae3c76949945bce79928fa7ef6846fc273b0480a657f579021c977/mkdocstrings/python/issues/341 |
| og:image:alt | Description of the bug When a class inherits from a class defined in another package and inherited_members is enabled, mkdocstrings renders the base class member's docstring under the subclass. If ... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | llucax |
| hostname | github.com |
| expected-hostname | github.com |
| None | 5713ecd83001b2f55c3509fb7a292bf7d66d0f167c7ae87a9f7eaec5a52bf8db |
| 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 | 653be32ff5c755082a4db662491bfbbcfc9112c1 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
| react-app-name | issues |
Links:
Viewport: width=device-width