Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

autodoc confuses constructor parameter typehint with class attribute typehint #11207

Open
jmandreoli opened this issue Feb 23, 2023 · 3 comments

Comments

@jmandreoli
Copy link

Describe the bug

The following script

class MyClass:
  "An instance of this class holds a list.\n\n:param r: the list as a string"
  l: list
  def __init__(self,r:str): self.l = list(r)

produces the following doc, as expected:

An instance of this class holds a list.
Parameters * r (str): the list as a string

Now, if I just change the name of the class attribute (and name it the same as the parameter), then Sphinx gets confused.

class MyClass:
  "An instance of this class holds a list.\n\n:param r: the list as a string"
  r: list
  def __init__(self,r:str): self.r = list(r)

produces the following doc, where the parameter is shown as having the type of the attribute with same name:

An instance of this class holds a list.
Parameters * r (list): the list as a string

Having a class attribute and a constructor parameter with the same name but not exactly the same type is quite common. For example, the parameter could be of type Optional[T] and the attribute of type T, with a default value being assigned in the constructor.

How to Reproduce

In index.rst:

.. toctree::
   :maxdepth: 2
   :caption: Contents:

.. automodule:: TEST
   :members:
   :member-order: bysource
   :show-inheritance:

In conf.py:

extensions = ['sphinx.ext.autodoc','sphinx.ext.viewcode']
autodoc_typehints = 'description'

Environment Information

Platform:              linux; (Linux-6.1.12-100.fc36.x86_64-x86_64-with-glibc2.35)
Python version:        3.10.9 | packaged by conda-forge | (main, Feb  2 2023, 20:20:04) [GCC 11.3.0])
Python implementation: CPython
Sphinx version:        6.1.3
Docutils version:      0.18.1
Jinja2 version:        3.1.2
Pygments version:      2.14.0

Sphinx extensions

No response

Additional context

No response

@greschd
Copy link

greschd commented Oct 21, 2024

A full reproducing example for this issue can be found here: https://github.com/greschd/sphinx-autodoc-typehints-confusion

@greschd
Copy link

greschd commented Oct 22, 2024

This immediate issue could be resolved with the following patch

diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py
index c925485e1..8c90ccb10 100644
--- a/sphinx/util/inspect.py
+++ b/sphinx/util/inspect.py
@@ -666,6 +666,11 @@ def signature(
         localns = TypeAliasNamespace(type_aliases)
         annotations = typing.get_type_hints(subject, None, localns, include_extras=True)
         for i, param in enumerate(parameters):
+            # Skip parameters which have annotations already. This is needed to avoid
+            # overwriting annotations of a class __init__ with annotations of class
+            # attributes.
+            if param.annotation is not inspect.Parameter.empty:
+                continue
             if param.name in annotations:
                 annotation = annotations[param.name]
                 if isinstance(annotation, TypeAliasForwardRef):

This skips overwriting the annotation in sphinx.util.inspect.signature if there is already an annotation given by inspect.signature.

I'd be happy to create a PR and add some tests, but don't have enough context to know whether this change might break things elsewhere. The most obvious case would be if a project relies on the current overwriting behavior. Maybe this should be behind an option?

@AA-Turner, maybe you could comment on this?

@greschd
Copy link

greschd commented Oct 22, 2024

In a more complex example, I have observed that napoleon_attr_annotations = True also has the effect of overwriting the annotations; this may need a similar treatment.

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

No branches or pull requests

3 participants