2727
2828from docutils .nodes import Text , citation , comment , inline , reference , section
2929from sphinx .addnodes import desc_content , pending_xref
30+ from sphinx .application import Sphinx as SphinxApp
3031from sphinx .util import logging
3132
3233from . import __version__
@@ -52,7 +53,7 @@ def _traverse_or_findall(node, condition, **kwargs):
5253 )
5354
5455
55- def rename_references (app , what , name , obj , options , lines ):
56+ def rename_references (app : SphinxApp , what , name , obj , options , lines ):
5657 # decorate reference numbers so that there are no duplicates
5758 # these are later undecorated in the doctree, in relabel_references
5859 references = set ()
@@ -114,7 +115,7 @@ def is_docstring_section(node):
114115 return False
115116
116117
117- def relabel_references (app , doc ):
118+ def relabel_references (app : SphinxApp , doc ):
118119 # Change 'hash-ref' to 'ref' in label text
119120 for citation_node in _traverse_or_findall (doc , citation ):
120121 if not _is_cite_in_numpydoc_docstring (citation_node ):
@@ -141,7 +142,7 @@ def matching_pending_xref(node):
141142 ref .replace (ref_text , new_text .copy ())
142143
143144
144- def clean_backrefs (app , doc , docname ):
145+ def clean_backrefs (app : SphinxApp , doc , docname ):
145146 # only::latex directive has resulted in citation backrefs without reference
146147 known_ref_ids = set ()
147148 for ref in _traverse_or_findall (doc , reference , descend = True ):
@@ -161,7 +162,7 @@ def clean_backrefs(app, doc, docname):
161162DEDUPLICATION_TAG = " !! processed by numpydoc !!"
162163
163164
164- def mangle_docstrings (app , what , name , obj , options , lines ):
165+ def mangle_docstrings (app : SphinxApp , what , name , obj , options , lines ):
165166 if DEDUPLICATION_TAG in lines :
166167 return
167168 show_inherited_class_members = app .config .numpydoc_show_inherited_class_members
@@ -190,6 +191,19 @@ def mangle_docstrings(app, what, name, obj, options, lines):
190191 title_re = re .compile (pattern , re .IGNORECASE | re .DOTALL )
191192 lines [:] = title_re .sub ("" , u_NL .join (lines )).split (u_NL )
192193 else :
194+ # Test the obj to find the module path, and skip the check if it's path is matched by
195+ # numpydoc_validation_exclude_files
196+ if app .config .numpydoc_validation_exclude_files :
197+ excluder = app .config .numpydoc_validation_files_excluder
198+ module = getattr (obj , "__module__" , None )
199+ if module :
200+ # Perform the exclusion check solely on the module if there's no __path__.
201+ path = getattr (obj , "__path__" , module )
202+ exclude_from_validation = excluder .search (path ) if excluder else False
203+ if exclude_from_validation :
204+ # Skip validation for this object.
205+ return
206+
193207 try :
194208 doc = get_doc_object (
195209 obj , what , u_NL .join (lines ), config = cfg , builder = app .builder
@@ -239,7 +253,7 @@ def mangle_docstrings(app, what, name, obj, options, lines):
239253 lines += [".." , DEDUPLICATION_TAG ]
240254
241255
242- def mangle_signature (app , what , name , obj , options , sig , retann ):
256+ def mangle_signature (app : SphinxApp , what , name , obj , options , sig , retann ):
243257 # Do not try to inspect classes that don't define `__init__`
244258 if inspect .isclass (obj ) and (
245259 not hasattr (obj , "__init__" )
@@ -273,7 +287,7 @@ def _clean_text_signature(sig):
273287 return start_sig + sig + ")"
274288
275289
276- def setup (app , get_doc_object_ = get_doc_object ):
290+ def setup (app : SphinxApp , get_doc_object_ = get_doc_object ):
277291 if not hasattr (app , "add_config_value" ):
278292 return None # probably called by nose, better bail out
279293
@@ -299,6 +313,7 @@ def setup(app, get_doc_object_=get_doc_object):
299313 app .add_config_value ("numpydoc_xref_ignore" , set (), True , types = [set , str ])
300314 app .add_config_value ("numpydoc_validation_checks" , set (), True )
301315 app .add_config_value ("numpydoc_validation_exclude" , set (), False )
316+ app .add_config_value ("numpydoc_validation_exclude_files" , set (), False )
302317 app .add_config_value ("numpydoc_validation_overrides" , dict (), False )
303318
304319 # Extra mangling domains
@@ -309,7 +324,7 @@ def setup(app, get_doc_object_=get_doc_object):
309324 return metadata
310325
311326
312- def update_config (app , config = None ):
327+ def update_config (app : SphinxApp , config = None ):
313328 """Update the configuration with default values."""
314329 if config is None : # needed for testing and old Sphinx
315330 config = app .config
@@ -342,6 +357,21 @@ def update_config(app, config=None):
342357 )
343358 config .numpydoc_validation_excluder = exclude_expr
344359
360+ # Generate the regexp for files to ignore during validation
361+ if isinstance (config .numpydoc_validation_exclude_files , str ):
362+ raise ValueError (
363+ f"numpydoc_validation_exclude_files must be a container of strings, "
364+ f"e.g. [{ config .numpydoc_validation_exclude_files !r} ]."
365+ )
366+
367+ config .numpydoc_validation_files_excluder = None
368+ if config .numpydoc_validation_exclude_files :
369+ exclude_files_expr = re .compile (
370+ r"|" .join (exp for exp in config .numpydoc_validation_exclude_files )
371+ )
372+ config .numpydoc_validation_files_excluder = exclude_files_expr
373+
374+ # Generate the regexp for validation overrides
345375 for check , patterns in config .numpydoc_validation_overrides .items ():
346376 config .numpydoc_validation_overrides [check ] = re .compile (
347377 r"|" .join (exp for exp in patterns )
0 commit comments