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

MAINT: Changed class constructor __init__ GL08 reporting #592

Open
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

mattgebert
Copy link

Changed validate function in validate.py for empty docstring error GL08 ("The object does not have a docstring"), specifically for __init__ constructors. If an empty docstring for __init__, will first check if class docstring is satisfied for parameter mismatches, before deciding to report GL08 (if mismatches exist, i.e. incomplete documentation).

Implemented new docstring test classes in test_validate.py to verify correct functionality.
Also updated readthedocs format.rst and validation.rst to reflect new changes to constructor checking behavior.

Solves #591.

Note, I think a modification to the readthedocs https://numpydoc.readthedocs.io/en/latest/validation.html#ignoring-validation-checks-with-inline-comments ought to be made

P.s. First time pull requesting to a public repo, let me know if anything is insufficient.

@larsoner
Copy link
Collaborator

Had a quick look at the code and this looks reasonable to me!

Note, I think a modification to the readthedocs https://numpydoc.readthedocs.io/en/latest/validation.html#ignoring-validation-checks-with-inline-comments ought to be made

Sounds good, do you want to add it here?

@mattgebert
Copy link
Author

mattgebert commented Dec 17, 2024

@larsoner I've added new doc comments in the diff in format.rst and validation.rst. I'll repeat the modifications here:

format.rst -> Documenting classes

Use the same sections as outlined above (all except :ref:`Returns <returns>`
are applicable).  The constructor (``__init__``) should also be documented
here, the :ref:`Parameters <params>` section of the docstring details the
constructor's parameters. The class docstring does not need to be repeated  <----
in a constructor, but is optional. <-----

validation.rst -> Ignoring Validation Checks with Inline Comments

def __init__(self):  # numpydoc ignore=GL08
    pass

Note, if the :ref:`class <classdoc>` docstring properly documents the <----
constructor, the ``GL08`` will be ignored by default. <----

This is supported by the :ref:`CLI <validation_via_cli>`,
:ref:`pre-commit hook <pre_commit_hook>`, and
:ref:`Sphinx extension <validation_during_sphinx_build>`.

@larsoner
Copy link
Collaborator

@larsoner I've added new doc comments in the diff

I think you maybe forgot to push (?), I don't see the changes in the files changed tab

@larsoner
Copy link
Collaborator

Ohh wait nevermind you already made the changes before I just didn't notice. And failed to notice again! 🤦

Copy link
Collaborator

@larsoner larsoner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stefanv did you want to look?

Copy link
Contributor

@stefanv stefanv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The general gist of this is correct, I think. I left some comments that I think may help future developers (ourselves, likely 😅) know what we did here.

doc/format.rst Outdated Show resolved Hide resolved
doc/validation.rst Outdated Show resolved Hide resolved
numpydoc/tests/test_validate.py Outdated Show resolved Hide resolved
numpydoc/tests/test_validate.py Outdated Show resolved Hide resolved
@@ -1198,6 +1198,90 @@ def missing_whitespace_after_comma(self):
"""


class GoodConstructorInclusion:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it correct to have parameters both in class docstr and init? Maybe it's OK 🤔

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good point, particularly if both exist. I haven't captured this complexity.

I think it's logical to allow both; particularly as the class docstr can be comprehensive (i.e. listing atrributes and properties which wouldn't be listed on init). I think where this really makes a difference is in linting for UI interpret hints - for accessible hinting you want to enforce parameter documentation.

For example, Pylance (vscode) will use both the class docstr and init docstr to display documentation for references / initialisation. When a init docstr hasn't been provided, initialisation hinting defaults to the class docstr.

We might be missing one thing here. The current revision checks if the missing docstr belongs to a constructor, and if it is satisfied by the class docstr. Perhaps we should likewise check class docstrs, silencing parameters mismatch warnings if parameters are satisfied by an (existing) init docstr?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can perhaps think of that as some additional follow-up logic, if you want?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I meant separate PR, so as to not slow this one down.)

numpydoc/tests/test_validate.py Outdated Show resolved Hide resolved
numpydoc/tests/test_validate.py Outdated Show resolved Hide resolved
# Check if the object is a class and has a docstring in the constructor
if doc.name.endswith("__init__") and doc.is_function_or_method:
cls_name = doc.code_obj.__qualname__.split(".")[0]
cls = getattr(importlib.import_module(doc.code_obj.__module__), cls_name)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@larsoner I don't know enough about current internals to know if this is how we do things; no utility functions / class methods for this purpose?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like Validator._load_obj(f"{doc.code_obj.__module__}.{doc.code_obj.__qualname__}") might do it, or maybe simpler Validator._load_obj(doc.name[:-9]) could work. @mattgebert do you want to try it (probably the second one)?

Copy link
Contributor

@stefanv stefanv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@larsoner Would appreciate your eyes on lines 641 through 652. Otherwise LGTM.

doc/format.rst Outdated Show resolved Hide resolved
constructor's parameters.
constructor's parameters. While repetition is unnecessary, a docstring for
the class constructor (``__init__``) can be added optionally to provide
detailed initialization documentation.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We recommended only the class docstring before. How do documentation writers decide what to put in the two docstrings?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Above my introductory pay grade, but I would stick to recommending class docstring only? The changes only allow flexibility for a constructor docstring if desired.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes:

There should be one-- and preferably only one --obvious way to do it.

And all that :)

# Check if the object is a class and has a docstring in the constructor
if doc.name.endswith("__init__") and doc.is_function_or_method:
cls_name = doc.code_obj.__qualname__.split(".")[0]
cls = getattr(importlib.import_module(doc.code_obj.__module__), cls_name)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like Validator._load_obj(f"{doc.code_obj.__module__}.{doc.code_obj.__qualname__}") might do it, or maybe simpler Validator._load_obj(doc.name[:-9]) could work. @mattgebert do you want to try it (probably the second one)?

if "GL08" not in ignore_validation_comments:
report_GL08: bool = True
# Check if the object is a class and has a docstring in the constructor
if doc.name.endswith("__init__") and doc.is_function_or_method:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like this is a tiny bit safer

Suggested change
if doc.name.endswith("__init__") and doc.is_function_or_method:
if doc.name.endswith(".__init__") and doc.is_function_or_method:

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

Successfully merging this pull request may close these issues.

3 participants