diff --git a/.github/workflows/label-check.yml b/.github/workflows/label-check.yml index a0e2e5c0..5c4aee62 100644 --- a/.github/workflows/label-check.yml +++ b/.github/workflows/label-check.yml @@ -4,8 +4,10 @@ on: pull_request: types: - opened + - repoened - labeled - unlabeled + - synchronize env: LABELS: ${{ join( github.event.pull_request.labels.*.name, ' ' ) }} diff --git a/numpydoc/tests/test_validate.py b/numpydoc/tests/test_validate.py index 0191e82a..e09c4414 100644 --- a/numpydoc/tests/test_validate.py +++ b/numpydoc/tests/test_validate.py @@ -41,6 +41,16 @@ def test_get_validation_checks_validity(checks): _ = validate.get_validation_checks(checks) +class _DummyList(list): + """Dummy list class to test validation.""" + + +def test_no_file(): + """Test that validation can be done on functions made on the fly.""" + # Just a smoke test for now, will have a None filename + validate.validate("numpydoc.tests.test_validate._DummyList.clear") + + @pytest.mark.parametrize( ["file_contents", "expected"], [ diff --git a/numpydoc/validate.py b/numpydoc/validate.py index 481b309e..922f817f 100644 --- a/numpydoc/validate.py +++ b/numpydoc/validate.py @@ -7,9 +7,10 @@ """ from copy import deepcopy -from typing import Dict, List, Set +from typing import Dict, List, Set, Optional import ast import collections +import functools import importlib import inspect import os @@ -111,7 +112,15 @@ IGNORE_COMMENT_PATTERN = re.compile("(?:.* numpydoc ignore[=|:] ?)(.+)") -def extract_ignore_validation_comments(filepath: os.PathLike) -> Dict[int, List[str]]: +# This function gets called once per function/method to be validated. +# We have to balance memory usage with performance here. It shouldn't be too +# bad to store these `dict`s (they should be rare), but to be safe let's keep +# the limit low-ish. This was set by looking at scipy, numpy, matplotlib, +# and pandas and they had between ~500 and ~1300 .py files as of 2023-08-16. +@functools.lru_cache(maxsize=2000) +def extract_ignore_validation_comments( + filepath: Optional[os.PathLike], +) -> Dict[int, List[str]]: """ Extract inline comments indicating certain validation checks should be ignored. @@ -125,8 +134,12 @@ def extract_ignore_validation_comments(filepath: os.PathLike) -> Dict[int, List[ dict[int, list[str]] Mapping of line number to a list of checks to ignore. """ - with open(filepath) as file: - numpydoc_ignore_comments = {} + numpydoc_ignore_comments = {} + try: + file = open(filepath) + except (OSError, TypeError): # can be None, nonexistent, or unreadable + return numpydoc_ignore_comments + with file: last_declaration = 1 declarations = ["def", "class"] for token in tokenize.generate_tokens(file.readline):