77"""
88
99from copy import deepcopy
10- from typing import Dict , List , Set , Optional
10+ from typing import Dict , List , Set , Optional , Any
1111import ast
1212import collections
1313import functools
116116# We have to balance memory usage with performance here. It shouldn't be too
117117# bad to store these `dict`s (they should be rare), but to be safe let's keep
118118# the limit low-ish. This was set by looking at scipy, numpy, matplotlib,
119- # and pandas and they had between ~500 and ~1300 .py files as of 2023-08-16.
119+ # and pandas, and they had between ~500 and ~1300 .py files as of 2023-08-16.
120120@functools .lru_cache (maxsize = 2000 )
121121def extract_ignore_validation_comments (
122122 filepath : Optional [os .PathLike ],
@@ -212,7 +212,7 @@ def error(code, **kwargs):
212212 message : str
213213 Error message with variables replaced.
214214 """
215- return ( code , ERROR_MSGS [code ].format (** kwargs ) )
215+ return code , ERROR_MSGS [code ].format (** kwargs )
216216
217217
218218class Validator :
@@ -290,24 +290,30 @@ def source_file_name(self):
290290
291291 except TypeError :
292292 # In some cases the object is something complex like a cython
293- # object that can't be easily introspected. An it's better to
293+ # object that can't be easily introspected. And it's better to
294294 # return the source code file of the object as None, than crash
295295 pass
296296 else :
297297 return fname
298298
299+ # When calling validate, files are parsed twice
300+ @staticmethod
301+ @functools .lru_cache (maxsize = 4000 )
302+ def _getsourcelines (obj : Any ):
303+ return inspect .getsourcelines (obj )
304+
299305 @property
300306 def source_file_def_line (self ):
301307 """
302308 Number of line where the object is defined in its file.
303309 """
304310 try :
305311 if isinstance (self .code_obj , property ):
306- sourcelines = inspect . getsourcelines (self .code_obj .fget )
312+ sourcelines = self . _getsourcelines (self .code_obj .fget )
307313 elif isinstance (self .code_obj , functools .cached_property ):
308- sourcelines = inspect . getsourcelines (self .code_obj .func )
314+ sourcelines = self . _getsourcelines (self .code_obj .func )
309315 else :
310- sourcelines = inspect . getsourcelines (self .code_obj )
316+ sourcelines = self . _getsourcelines (self .code_obj )
311317 # getsourcelines will return the line of the first decorator found for the
312318 # current function. We have to find the def declaration after that.
313319 def_line = next (
@@ -320,7 +326,7 @@ def source_file_def_line(self):
320326 return sourcelines [- 1 ] + def_line
321327 except (OSError , TypeError ):
322328 # In some cases the object is something complex like a cython
323- # object that can't be easily introspected. An it's better to
329+ # object that can't be easily introspected. And it's better to
324330 # return the line number as None, than crash
325331 pass
326332
@@ -613,7 +619,7 @@ def validate(obj_name, validator_cls=None, **validator_kwargs):
613619 else :
614620 doc = validator_cls (obj_name = obj_name , ** validator_kwargs )
615621
616- # lineno is only 0 if we have a module docstring in the file and we are
622+ # lineno is only 0 if we have a module docstring in the file, and we are
617623 # validating that, so we change to 1 for readability of the output
618624 ignore_validation_comments = extract_ignore_validation_comments (
619625 doc .source_file_name
0 commit comments