1818"""
1919
2020import hashlib
21+ import importlib
2122import inspect
2223import itertools
2324import pydoc
2425import re
26+ import sys
2527from collections .abc import Callable
2628from copy import deepcopy
29+ from pathlib import Path
2730
2831from docutils .nodes import Text , citation , comment , inline , reference , section
2932from sphinx .addnodes import desc_content , pending_xref
33+ from sphinx .application import Sphinx as SphinxApp
3034from sphinx .util import logging
3135
3236from . import __version__
@@ -52,7 +56,7 @@ def _traverse_or_findall(node, condition, **kwargs):
5256 )
5357
5458
55- def rename_references (app , what , name , obj , options , lines ):
59+ def rename_references (app : SphinxApp , what , name , obj , options , lines ):
5660 # decorate reference numbers so that there are no duplicates
5761 # these are later undecorated in the doctree, in relabel_references
5862 references = set ()
@@ -114,7 +118,7 @@ def is_docstring_section(node):
114118 return False
115119
116120
117- def relabel_references (app , doc ):
121+ def relabel_references (app : SphinxApp , doc ):
118122 # Change 'hash-ref' to 'ref' in label text
119123 for citation_node in _traverse_or_findall (doc , citation ):
120124 if not _is_cite_in_numpydoc_docstring (citation_node ):
@@ -141,7 +145,7 @@ def matching_pending_xref(node):
141145 ref .replace (ref_text , new_text .copy ())
142146
143147
144- def clean_backrefs (app , doc , docname ):
148+ def clean_backrefs (app : SphinxApp , doc , docname ):
145149 # only::latex directive has resulted in citation backrefs without reference
146150 known_ref_ids = set ()
147151 for ref in _traverse_or_findall (doc , reference , descend = True ):
@@ -161,7 +165,7 @@ def clean_backrefs(app, doc, docname):
161165DEDUPLICATION_TAG = " !! processed by numpydoc !!"
162166
163167
164- def mangle_docstrings (app , what , name , obj , options , lines ):
168+ def mangle_docstrings (app : SphinxApp , what , name , obj , options , lines ):
165169 if DEDUPLICATION_TAG in lines :
166170 return
167171 show_inherited_class_members = app .config .numpydoc_show_inherited_class_members
@@ -190,6 +194,38 @@ def mangle_docstrings(app, what, name, obj, options, lines):
190194 title_re = re .compile (pattern , re .IGNORECASE | re .DOTALL )
191195 lines [:] = title_re .sub ("" , u_NL .join (lines )).split (u_NL )
192196 else :
197+ # Test the obj to find the module path, and skip the check if it's path is matched by
198+ # numpydoc_validation_exclude_files
199+ if (
200+ app .config .numpydoc_validation_exclude_files
201+ and app .config .numpydoc_validation_checks
202+ ):
203+ excluder = app .config .numpydoc_validation_files_excluder
204+ module = inspect .getmodule (obj )
205+ try :
206+ # Get the module relative path from the name
207+ if module :
208+ mod_path = Path (module .__file__ )
209+ package_rel_path = mod_path .parent .relative_to (
210+ Path (
211+ importlib .import_module (
212+ module .__name__ .split ("." )[0 ]
213+ ).__file__
214+ ).parent
215+ ).as_posix ()
216+ module_file = mod_path .as_posix ().replace (
217+ mod_path .parent .as_posix (), ""
218+ )
219+ path = package_rel_path + module_file
220+ else :
221+ path = None
222+ except AttributeError as e :
223+ path = None
224+
225+ if path and excluder and excluder .search (path ):
226+ # Skip validation for this object.
227+ return
228+
193229 try :
194230 doc = get_doc_object (
195231 obj , what , u_NL .join (lines ), config = cfg , builder = app .builder
@@ -239,7 +275,7 @@ def mangle_docstrings(app, what, name, obj, options, lines):
239275 lines += [".." , DEDUPLICATION_TAG ]
240276
241277
242- def mangle_signature (app , what , name , obj , options , sig , retann ):
278+ def mangle_signature (app : SphinxApp , what , name , obj , options , sig , retann ):
243279 # Do not try to inspect classes that don't define `__init__`
244280 if inspect .isclass (obj ) and (
245281 not hasattr (obj , "__init__" )
@@ -273,7 +309,7 @@ def _clean_text_signature(sig):
273309 return start_sig + sig + ")"
274310
275311
276- def setup (app , get_doc_object_ = get_doc_object ):
312+ def setup (app : SphinxApp , get_doc_object_ = get_doc_object ):
277313 if not hasattr (app , "add_config_value" ):
278314 return None # probably called by nose, better bail out
279315
@@ -299,6 +335,7 @@ def setup(app, get_doc_object_=get_doc_object):
299335 app .add_config_value ("numpydoc_xref_ignore" , set (), True , types = [set , str ])
300336 app .add_config_value ("numpydoc_validation_checks" , set (), True )
301337 app .add_config_value ("numpydoc_validation_exclude" , set (), False )
338+ app .add_config_value ("numpydoc_validation_exclude_files" , set (), False )
302339 app .add_config_value ("numpydoc_validation_overrides" , dict (), False )
303340
304341 # Extra mangling domains
@@ -309,7 +346,7 @@ def setup(app, get_doc_object_=get_doc_object):
309346 return metadata
310347
311348
312- def update_config (app , config = None ):
349+ def update_config (app : SphinxApp , config = None ):
313350 """Update the configuration with default values."""
314351 if config is None : # needed for testing and old Sphinx
315352 config = app .config
@@ -342,6 +379,21 @@ def update_config(app, config=None):
342379 )
343380 config .numpydoc_validation_excluder = exclude_expr
344381
382+ # Generate the regexp for files to ignore during validation
383+ if isinstance (config .numpydoc_validation_exclude_files , str ):
384+ raise ValueError (
385+ f"numpydoc_validation_exclude_files must be a container of strings, "
386+ f"e.g. [{ config .numpydoc_validation_exclude_files !r} ]."
387+ )
388+
389+ config .numpydoc_validation_files_excluder = None
390+ if config .numpydoc_validation_exclude_files :
391+ exclude_files_expr = re .compile (
392+ r"|" .join (exp for exp in config .numpydoc_validation_exclude_files )
393+ )
394+ config .numpydoc_validation_files_excluder = exclude_files_expr
395+
396+ # Generate the regexp for validation overrides
345397 for check , patterns in config .numpydoc_validation_overrides .items ():
346398 config .numpydoc_validation_overrides [check ] = re .compile (
347399 r"|" .join (exp for exp in patterns )
0 commit comments