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

Unexpected output when class attribute and constructor arg have same name (napoleon bug) #308

Closed
hoodmane opened this issue Jan 20, 2023 · 3 comments · Fixed by #309
Closed

Comments

@hoodmane
Copy link
Collaborator

hoodmane commented Jan 20, 2023

This is an upstream bug: sphinx-doc/sphinx#11143

Suppose we have the following class:

class Example:
    """
    A Class

    Parameters
    ----------
    blah:
        Description of parameter blah
    """
    def __init__(self, blah: CodeType):
        pass

    blah: ModuleType

We get the following output (just the parameters):

   Parameters:
      **blah** (*module*) -- Description of parameter blah

Expected output:

   Parameters:
      **blah** ("CodeType") -- Description of parameter blah

If you want I can provide a monkey patch that implements my fix in sphinx-doc/sphinx#11143 on existing versions of sphinx. I'm not sure how far we want to go in patching with upstream bugs.

@gaborbernat
Copy link
Member

Unless upstream rejects the PR I see no reason to do it here 🤔

@hoodmane
Copy link
Collaborator Author

hoodmane commented Jan 20, 2023

Well the current behavior is quite clearly wrong so I sure hope they'll accept it. In my experience they are a bit slow about reviewing things. I'll open a PR.

Actually, one thing worth noting is that if we got the upstream change in, we would end up with the parameter type rendered as *code* (and no link if we use intersphinx/html) rather than "CodeType" (and a link). For some reason, Napoleon will (incorrectly) fetch parameter types for class constructor args but not for parameters or function / method args. I think we might just want to disable this upstream constructor parameter type fetching.

In an ideal world, it would emit a napoleon-render-type event called with the object to produce a text representative of the object.

gaborbernat pushed a commit that referenced this issue Jan 20, 2023
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Resolves #308
@caffeinepills
Copy link
Contributor

caffeinepills commented Apr 5, 2024

#309 broke Google style annotations marked as Attributes: from showing types in class header docstrings. (napoleon_attr_annotations = True)

For those who ran into this you will have to unpatch the patch:

import sphinx_autodoc_typehints
def _revert_patch():
    pass

sphinx_autodoc_typehints.patches._patch_google_docstring_lookup_annotation = _revert_patch

Unfortunately it doesn't apply the same style to the types without some overriding of GoogleDocstring._parse_attributes_section, but atleast you can get your types back into the header.

To patch the _parse_attributes_section to get the style, you can override it as follows:

    def _parse_attributes_section(self, section: str) -> list[str]:
        lines = []
        for _name, _type, _desc in self._consume_fields():
            if not _type:
                _type = self._lookup_annotation(_name)
            if self._config.napoleon_use_ivar:
                field = ':ivar %s: ' % _name
                lines.extend(self._format_block(field, _desc))
                if _type:
                    lines.append(f':vartype {_name}: {_type}')
            else:
                lines.append('.. attribute:: ' + _name)
                if self._opt:
                    if 'no-index' in self._opt or 'noindex' in self._opt:
                        lines.append('   :no-index:')
                lines.append('')

                fields = self._format_field('', '', _desc)
                lines.extend(self._indent(fields, 3))
                if _type:
                    lines.append('')
                    lines.extend(self._indent([':type: %s' % _type], 3))
                lines.append('')
        if self._config.napoleon_use_ivar:
            lines.append('')
        return lines

This is the original, you can patch it to be as follows:

    def _parse_attributes_section(self, section: str) -> list[str]:
        lines = []
        for _name, _type, _desc in self._consume_fields():
            if not _type:
                _type = self._lookup_annotation(_name)
                if _name in self._annotations:
                    _type = sphinx_autodoc_typehints.add_type_css_class(
                       sphinx_autodoc_typehints.format_annotation(self._annotations[_name], self._app.config))
            if self._config.napoleon_use_ivar:
                field = ':ivar %s: ' % _name
                lines.extend(self._format_block(field, _desc))
                if _type:
                    lines.append(f':vartype {_name}: {_type}')
            else:
                lines.append('.. attribute:: ' + _name)
                if self._opt:
                    if 'no-index' in self._opt or 'noindex' in self._opt:
                        lines.append('   :no-index:')
                lines.append('')

                fields = self._format_field('', '', _desc)
                lines.extend(self._indent(fields, 3))
                if _type:
                    lines.append('')
                    lines.extend(self._indent([':type: %s' % _type], 3))
                lines.append('')
        if self._config.napoleon_use_ivar:
            lines.append('')
        return lines
        
GoogleDocstring._parse_attributes_section = _parse_attributes_section

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

Successfully merging a pull request may close this issue.

3 participants