Skip to content

bug: merge_init_into_class renders inherited __init__ source (leaks site-packages paths) #333

Description

@cdwilson

Description of the bug

With merge_init_into_class: true, the source block in
templates/material/_base/class.html.jinja unconditionally prefers
all_members["__init__"] over the class itself:

{%ifconfig.merge_init_into_class%}{%if"__init__"inall_membersandall_members["__init__"].source%}

all_members includes inherited members, so when a class does not
define __init__ itself (e.g. any pydantic BaseModel subclass, with
preload_modules: [pydantic] so griffe can resolve the base), the
rendered source block is pydantic.BaseModel.__init__ with a summary
like:

Source code in .venv/lib/python3.10/site-packages/pydantic/main.py

instead of the documented class's own file. Two problems:

  1. The environment-specific .venv/... path leaks into published docs.
  2. The source shown is the base class's __init__, which is not the
    documented object's code at all.

Additionally, when the merged __init__ has no source, the block
renders nothing rather than falling back to the class source.

To Reproduce

Verified with mkdocs 1.6.1, mkdocstrings 1.0.4, mkdocstrings-python
2.0.5, griffe 2.1.0, pydantic 2.13.4 on Python 3.10.

mkdir -p repro/src/pkg repro/docs &&cd repro
python -m venv .venv &&. .venv/bin/activate
pip install mkdocs mkdocs-material "mkdocstrings[python]" pydantic

src/pkg/__init__.py:

frompydanticimportBaseModelclassModel(BaseModel):
"""A pydantic model without its own `__init__`."""x: int=0

docs/index.md:

::: pkg.Model

mkdocs.yml:

site_name: reprotheme:
name: materialplugins:
- mkdocstrings:
handlers:
python:
paths: [src]options:
merge_init_into_class: truepreload_modules: [pydantic]show_source: true

Build and inspect the rendered source block:

$ mkdocs build
$ grep -o "Source code in <code>[^<]*</code>" site/index.htmlSource code in <code>.venv/lib/python3.10/site-packages/pydantic/main.py</code>

Expected: src/pkg/__init__.py.

The mechanism, shown at the griffe level (run from the same directory):

importsysconfigimportgriffeloader=griffe.GriffeLoader(
search_paths=["src", sysconfig.get_paths()["purelib"]]
)
loader.load("pydantic")
pkg=loader.load("pkg")
loader.resolve_aliases()
cls=pkg["Model"]
init=cls.all_members["__init__"]
print(cls.relative_filepath) # src/pkg/__init__.pyprint(init.inherited) # Trueprint(init.relative_filepath) # .venv/lib/python3.10/site-packages/pydantic/main.py

Expected behavior

Only use __init__'s source when the class defines it itself
(griffe exposes inherited on the member); otherwise fall back to the
class's own source.

Environment information

python -m mkdocstrings_handlers.python._internal.debug # | xclip -selection clipboard
  • System: macOS-26.5.1-arm64-arm-64bit
  • Python: cpython 3.10.20 (/.venv/bin/python3)
  • Environment variables:
  • Installed packages:
    • mkdocstrings-python v2.0.5

Additional context

I'm using a template override in
docs/templates/python/materialx/class.html.jinja, wired
up via the custom_templates option in mkdocs.yaml:

{%extends"_base/class.html.jinja"%}{%blocksource%}{%import"language.html.jinja"aslangwithcontext%}{%ifconfig.show_source%}{%ifconfig.merge_init_into_classand"__init__"inall_membersandnotall_members["__init__"].inheritedandall_members["__init__"].source%}{%withinit = all_members["__init__"] %}
<detailsclass="mkdocstrings-source">
<summary>{{ lang.t("Source code in") }} <code>
{%- ifinit.relative_filepath.is_absolute() -%}
{{ init.relative_package_filepath }}
{%- else -%}
{{ init.relative_filepath }}
{%- endif -%}
</code></summary>
{{ init.source|highlight(language="python", linestart=init.lineno or 0, linenums=True) }}
</details>
{%endwith%}{%elifclass.source%}
<detailsclass="mkdocstrings-source">
<summary>{{ lang.t("Source code in") }} <code>
{%- ifclass.relative_filepath.is_absolute() -%}
{{ class.relative_package_filepath }}
{%- else -%}
{{ class.relative_filepath }}
{%- endif -%}
</code></summary>
{{ class.source|highlight(language="python", linestart=class.lineno or 0, linenums=True) }}
</details>
{%endif%}{%endif%}{%endblocksource%}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

featureNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions