Skip to content

Constructor checking for AST validator #622

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

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

Conversation

mattgebert
Copy link
Contributor

@mattgebert mattgebert commented May 11, 2025

Modifies validate.py to include checks for constructors methods (class.__init__) implemented by class docstring when using the AST validator, which is essential for hook use (probably the majority use case for numpydoc). Also fixes nested class references (previously implemented incorrectly).

This pull request now adds checks for the AST class instance, and if using the AST, splits the names of the submodule nested classes, and finds the parent through a AST node walk procedure, before allowing the cls_doc check for parameter mismatches.

Solution to #591 (update from #592) and fixed inconsistency in #605.

mattgebert and others added 6 commits April 6, 2025 00:40
…ructor

Introduced GL08 checking for constructors (i.e. __init__) but didn't traverse the parent class heireacy when importing the __init__ parent from a module.
…checking for initialisation docstring

Test written due to bug missed in numpy#592.
…rings compliance with parent class

The abstract syntax tree doesn't provide any link to a parent class at node level, so special traversal is required to check constructor parent implementation of docstrings.
…ule tests for AST (AbstractSyntaxTree) discovery of constructor method parent class.
…ase installation, and changed pytest invocation to use `numpydoc` instead of `.`, for consistency with `test` job

In response to discussion on numpy#591
@mattgebert
Copy link
Contributor Author

I've noted that even despite using --pre for pre-release versions of dependencies, we're still not running hook tests in the pre-release jobs. In fact, I can see that the hook tests are not even being collected (though this is working perfectly on my local machine).... I will try adding an explicit call to pytest for hooks.

All pytest unit testing, including prerelease and test jobs, missing the hook pytest collection (link to recent check):
image

Local machine collecting the pytest collection
image
3 extra files:

  • example_module.py
  • test_utils.py
  • test_validate_hook.py

Comment on lines +650 to +684
if doc.name.endswith(".__init__") and doc.is_function_or_method:
from numpydoc.hooks.validate_docstrings import (
AstValidator, # Support abstract syntax tree hook.
)

if hasattr(doc, "code_obj"):
cls_name = ".".join(
doc.code_obj.__qualname__.split(".")[:-1]
) # Collect all class depths before the constructor.
cls = Validator._load_obj(f"{doc.code_obj.__module__}.{cls_name}")
# cls = Validator._load_obj(f"{doc.name[:-9]}.{cls_name}") ## Alternative
cls_doc = Validator(get_doc_object(cls))
elif isinstance(doc, AstValidator): # Supports class traversal for ASTs.
names = doc._name.split(".")

if len(names) > 2: # i.e. module.class.__init__
nested_cls_names = names[1:-1] # class1,class2 etc.
cls_name = names[-2]
filename = doc.source_file_name # from the AstValidator object

# Use AST to find the class node...
with open(filename) as file:
module_node = ast.parse(file.read(), filename)

# Recursively find each subclass from the module node.
next_node = module_node
for name in nested_cls_names:
next_node = _find_class_node(next_node, name)
# Get the documentation.
cls_doc = AstValidator(
ast_node=next_node, filename=filename, obj_name=cls_name
)
else:
# Ignore edge case: __init__ functions that don't belong to a class.
cls_doc = None
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this is the way to do this. The DocstringVisitor class is already traversing the tree and capturing node ancestry (currently, just the name, but after #623, it will be all AST node ancestors). This is then used to instantiate the AstValidator, so we should be able to use that information to modify the check for what you are trying to achieve. A lot of the logic here is pretty much doing what those classes are doing, but for a second time, which I would like to avoid.

You may be able to simply filter out any cases that shouldn't be flagged in DocstringVisitor._get_numpydoc_issues(), at which point you would have access to the AST node and its ancestry. This is how the ignore rules are applied for the hook.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Brilliant - I was actually hoping this could be the case prior to making these changes.

Once #623 is merged I'll be happy update this logic and use the ancestry.

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 this pull request may close these issues.

2 participants