python icon indicating copy to clipboard operation
python copied to clipboard

bug: Class init parameter name disappears from attribute docstring using `separate_signature`

Open connesy opened this issue 1 year ago • 6 comments

Description of the bug

When calling a method on an input argument to a class __init__ while having separate_signature: true, the parameter name disappears from the rendered instance attribute docstring, leaving only the attribute name and the method called on the input argument.

To Reproduce

python -m venv .venv
. .venv/bin/activate
pip install mkdocstrings-python
python
# mkdocs.yml
site_name: test-site
plugins:
  - mkdocstrings:
      default_handler: python
      handlers:
        python:
          options:
            separate_signature: true
<!-- test.md -->
::: test
# test.py

class SomeClass:
    """This is a class that does something."""

    def __init__(self, some_float: float) -> None:
        self.as_hex = some_float.hex()
        """The hex value of the input float."""

Expected behavior

image

I expect the rendered docstring for the as_hex instance attribute to say as_hex = some_float.hex().

Actual behavior

image

With separate_signature: true, the some_float part disappears, and what is rendered is only as_hex = hex().

Environment information

python -m mkdocstrings_handlers.python.debug  # | xclip -selection clipboard
  • System: Linux-6.8.0-49-generic-x86_64-with-glibc2.39
  • Python: cpython 3.13.1 (/home/stefan/test-mkdocstrings/.venv/bin/python)
  • Environment variables:
  • Installed packages:
    • mkdocs v1.6.1
    • mkdocstrings v0.27.0
    • mkdocstrings-python v1.12.2
    • griffe v1.5.1

connesy avatar Dec 11 '24 13:12 connesy

Thanks for the report @connesy.

This behavior is configurable with this setting: https://mkdocstrings.github.io/python/usage/configuration/signatures/#annotations_path.

Note though that we already have one special case, for enumeration values: Class.ENUM_VALUE.value will not be shortened to value (which wouldn't be cross-reference'able) but to ENUM_VALUE.value. We could consider doing the same thing for other kinds of objects. I suppose some_float here is a float? Not sure how hard it would be.

Or maybe attribute values should never be shortened! Or configured with a different setting than annotations_path.

pawamoy avatar Dec 11 '24 14:12 pawamoy

I suppose some_float here is a float?

Yes, there's a reproducible example in the details of my issue.

connesy avatar Dec 11 '24 15:12 connesy

This behavior is configurable with this setting: https://mkdocstrings.github.io/python/usage/configuration/signatures/#annotations_path.

I would think this would be a different setting (if it existed) thanannotations_path, since this is not about the type annotation of the argument, but the actual value of the instance attribute.

Or maybe attribute values should never be shortened!

I would not expect attribute values to be shortened (more than what is in the source document). If my __init__ had from pathlib import Path and takes a some_path: pathlib.Path() parameter instead, and set e.g. self.resolved_path = some_path.resolve(), then I would expect the type annotation to show Path and the attribute value to be some_path.resolve(), not resolve().

connesy avatar Dec 11 '24 15:12 connesy

Yeah, I agree that would be more sensible. And that would even allow us to get rid of the special casing for enum values.

pawamoy avatar Dec 11 '24 16:12 pawamoy

I guess it could follow the value of annotations_path, but only shorten objects paths, not method and attribute paths.

import module.submodule

class SomeClass:
    def __init__(self, value: module.submodule.SomeType):
        self.parsed_value = module.submodule.module_instance.method(value)

In this case, the type annotation will be shortened (or not) based on annotations_path, but so could parsed_value, e.g. module_instance.method(value) for brief and module.submodule.module_instance.method(value) for full.

connesy avatar Dec 11 '24 16:12 connesy

So basically removing all parts that are modules. I can imagine a few edge cases where users would still want to show the module parts because otherwise the rest would be ambiguous (several different modules providing a function with the same name for example). The safest bet here is to render what's in the source. Any other "smart" shortening would feel a bit arbitrary IMO.

I'll review existing docs to see how this setting is used, and how it affects attributes rendering, and whether we can easily change it without breaking use-cases.

pawamoy avatar Dec 11 '24 16:12 pawamoy