7
7
"""
8
8
9
9
from copy import deepcopy
10
- from typing import Dict , List , Set , Optional
10
+ from typing import Dict , List , Set , Optional , Any
11
11
import ast
12
12
import collections
13
13
import functools
116
116
# We have to balance memory usage with performance here. It shouldn't be too
117
117
# bad to store these `dict`s (they should be rare), but to be safe let's keep
118
118
# 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.
120
120
@functools .lru_cache (maxsize = 2000 )
121
121
def extract_ignore_validation_comments (
122
122
filepath : Optional [os .PathLike ],
@@ -212,7 +212,7 @@ def error(code, **kwargs):
212
212
message : str
213
213
Error message with variables replaced.
214
214
"""
215
- return ( code , ERROR_MSGS [code ].format (** kwargs ) )
215
+ return code , ERROR_MSGS [code ].format (** kwargs )
216
216
217
217
218
218
class Validator :
@@ -290,24 +290,30 @@ def source_file_name(self):
290
290
291
291
except TypeError :
292
292
# 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
294
294
# return the source code file of the object as None, than crash
295
295
pass
296
296
else :
297
297
return fname
298
298
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
+
299
305
@property
300
306
def source_file_def_line (self ):
301
307
"""
302
308
Number of line where the object is defined in its file.
303
309
"""
304
310
try :
305
311
if isinstance (self .code_obj , property ):
306
- sourcelines = inspect . getsourcelines (self .code_obj .fget )
312
+ sourcelines = self . _getsourcelines (self .code_obj .fget )
307
313
elif isinstance (self .code_obj , functools .cached_property ):
308
- sourcelines = inspect . getsourcelines (self .code_obj .func )
314
+ sourcelines = self . _getsourcelines (self .code_obj .func )
309
315
else :
310
- sourcelines = inspect . getsourcelines (self .code_obj )
316
+ sourcelines = self . _getsourcelines (self .code_obj )
311
317
# getsourcelines will return the line of the first decorator found for the
312
318
# current function. We have to find the def declaration after that.
313
319
def_line = next (
@@ -320,7 +326,7 @@ def source_file_def_line(self):
320
326
return sourcelines [- 1 ] + def_line
321
327
except (OSError , TypeError ):
322
328
# 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
324
330
# return the line number as None, than crash
325
331
pass
326
332
@@ -613,7 +619,7 @@ def validate(obj_name, validator_cls=None, **validator_kwargs):
613
619
else :
614
620
doc = validator_cls (obj_name = obj_name , ** validator_kwargs )
615
621
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
617
623
# validating that, so we change to 1 for readability of the output
618
624
ignore_validation_comments = extract_ignore_validation_comments (
619
625
doc .source_file_name
0 commit comments