pw_presubmit: Add with_filter() method to Check
Rename _Check to Check so it can be used as a decorator. Then add a
with_filter() method to Check objects, so it's simple to reuse imported
checks but apply additional filters to them.
Change-Id: I3917e2d7a5fa74577eba5599b024dcf4d102c112
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/60920
Reviewed-by: Wyatt Hepler <hepler@google.com>
Reviewed-by: Anthony DiGirolamo <tonymd@google.com>
Pigweed-Auto-Submit: Rob Mohr <mohrr@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
diff --git a/pw_presubmit/docs.rst b/pw_presubmit/docs.rst
index 063f522..139026d 100644
--- a/pw_presubmit/docs.rst
+++ b/pw_presubmit/docs.rst
@@ -118,9 +118,9 @@
Python Checks
=============
-There are two checks in the ``pw_presubmit.python_checks`` module, ``gn_lint``
+There are two checks in the ``pw_presubmit.python_checks`` module, ``gn_pylint``
and ``gn_python_check``. They assume there's a top-level ``python`` GN target.
-``gn_lint`` runs Pylint and Mypy checks and ``gn_python_check`` runs Pylint,
+``gn_pylint`` runs Pylint and Mypy checks and ``gn_python_check`` runs Pylint,
Mypy, and all Python tests.
Inclusive Language
@@ -228,7 +228,7 @@
# Include the upstream inclusive language check.
inclusive_language.inclusive_language,
# Include just the lint-related Python checks.
- python_checks.lint_checks(exclude=PATH_EXCLUSIONS),
+ python_checks.gn_pylint.with_filter(exclude=PATH_EXCLUSIONS),
)
FULL = (
@@ -236,7 +236,7 @@
release_build,
# Use the upstream Python checks, with custom path filters applied.
# Checks listed multiple times are only run once.
- python_checks.all_checks(exclude=PATH_EXCLUSIONS),
+ python_checks.gn_python_check.with_filter(exclude=PATH_EXCLUSIONS),
)
PROGRAMS = pw_presubmit.Programs(quick=QUICK, full=FULL)
diff --git a/pw_presubmit/py/pw_presubmit/pigweed_presubmit.py b/pw_presubmit/py/pw_presubmit/pigweed_presubmit.py
index 3b165a9..e8d292c 100755
--- a/pw_presubmit/py/pw_presubmit/pigweed_presubmit.py
+++ b/pw_presubmit/py/pw_presubmit/pigweed_presubmit.py
@@ -840,7 +840,7 @@
LINTFORMAT = (
_LINTFORMAT,
- python_checks.gn_lint,
+ python_checks.gn_python_lint,
)
QUICK = (
diff --git a/pw_presubmit/py/pw_presubmit/presubmit.py b/pw_presubmit/py/pw_presubmit/presubmit.py
index dc82110..d9baea9 100644
--- a/pw_presubmit/py/pw_presubmit/presubmit.py
+++ b/pw_presubmit/py/pw_presubmit/presubmit.py
@@ -37,6 +37,8 @@
See pigweed_presbumit.py for an example of how to define presubmit checks.
"""
+from __future__ import annotations
+
import collections
import contextlib
import dataclasses
@@ -251,12 +253,12 @@
return not failed and not skipped
def apply_filters(
- self, program: Sequence[Callable]
- ) -> List[Tuple['_Check', Sequence[Path]]]:
+ self,
+ program: Sequence[Callable]) -> List[Tuple[Check, Sequence[Path]]]:
"""Returns list of (check, paths) for checks that should run."""
- checks = [c if isinstance(c, _Check) else _Check(c) for c in program]
+ checks = [c if isinstance(c, Check) else Check(c) for c in program]
filter_to_checks: Dict[_Filter,
- List[_Check]] = collections.defaultdict(list)
+ List[Check]] = collections.defaultdict(list)
for check in checks:
filter_to_checks[check.filter].append(check)
@@ -265,9 +267,9 @@
return [(c, check_to_paths[c]) for c in checks if c in check_to_paths]
def _map_checks_to_paths(
- self, filter_to_checks: Dict[_Filter, List['_Check']]
- ) -> Dict['_Check', Sequence[Path]]:
- checks_to_paths: Dict[_Check, Sequence[Path]] = {}
+ self, filter_to_checks: Dict[_Filter, List[Check]]
+ ) -> Dict[Check, Sequence[Path]]:
+ checks_to_paths: Dict[Check, Sequence[Path]] = {}
posix_paths = tuple(p.as_posix() for p in self._relative_paths)
@@ -469,7 +471,11 @@
return presubmit.run(program, keep_going)
-class _Check:
+def _make_str_tuple(value: Union[Iterable[str], str]) -> Tuple[str, ...]:
+ return tuple([value] if isinstance(value, str) else value)
+
+
+class Check:
"""Wraps a presubmit check function.
This class consolidates the logic for running and logging a presubmit check.
@@ -485,9 +491,22 @@
self.filter: _Filter = path_filter
self.always_run: bool = always_run
- # Since _Check wraps a presubmit function, adopt that function's name.
+ # Since Check wraps a presubmit function, adopt that function's name.
self.__name__ = self._check.__name__
+ def with_filter(
+ self,
+ endswith: Iterable[str] = '',
+ exclude: Iterable[Union[Pattern[str], str]] = ()
+ ) -> Check:
+ return Check(check_function=self._check,
+ path_filter=_Filter(endswith=self.filter.endswith +
+ _make_str_tuple(endswith),
+ exclude=self.filter.exclude +
+ tuple(re.compile(e)
+ for e in exclude)),
+ always_run=self.always_run)
+
@property
def name(self):
return self.__name__
@@ -530,7 +549,7 @@
return _Result.PASS
def __call__(self, ctx: PresubmitContext, *args, **kwargs):
- """Calling a _Check calls its underlying function directly.
+ """Calling a Check calls its underlying function directly.
This makes it possible to call functions wrapped by @filter_paths. The
prior filters are ignored, so new filters may be applied.
@@ -564,13 +583,9 @@
if required_args else ''))
-def _make_str_tuple(value: Iterable[str]) -> Tuple[str, ...]:
- return tuple([value] if isinstance(value, str) else value)
-
-
-def filter_paths(endswith: Iterable[str] = (''),
+def filter_paths(endswith: Iterable[str] = '',
exclude: Iterable[Union[Pattern[str], str]] = (),
- always_run: bool = False) -> Callable[[Callable], _Check]:
+ always_run: bool = False) -> Callable[[Callable], Check]:
"""Decorator for filtering the paths list for a presubmit check function.
Path filters only apply when the function is used as a presubmit check.
@@ -586,10 +601,10 @@
a wrapped version of the presubmit function
"""
def filter_paths_for_function(function: Callable):
- return _Check(function,
- _Filter(_make_str_tuple(endswith),
- tuple(re.compile(e) for e in exclude)),
- always_run=always_run)
+ return Check(function,
+ _Filter(_make_str_tuple(endswith),
+ tuple(re.compile(e) for e in exclude)),
+ always_run=always_run)
return filter_paths_for_function
diff --git a/pw_presubmit/py/pw_presubmit/python_checks.py b/pw_presubmit/py/pw_presubmit/python_checks.py
index 67295d0..f793fec 100644
--- a/pw_presubmit/py/pw_presubmit/python_checks.py
+++ b/pw_presubmit/py/pw_presubmit/python_checks.py
@@ -46,12 +46,17 @@
# TODO(mohrr) Remove gn_check=False when it passes for all downstream projects.
@filter_paths(endswith=_PYTHON_EXTENSIONS)
-def gn_lint(ctx: pw_presubmit.PresubmitContext) -> None:
+def gn_python_lint(ctx: pw_presubmit.PresubmitContext) -> None:
build.gn_gen(ctx.root, ctx.output_dir, gn_check=False)
build.ninja(ctx.output_dir, 'python.lint')
-_LINT_CHECKS = (gn_lint, )
+# TODO(mohrr) Remove gn_lint when downstream projects no longer reference it.
+gn_lint = gn_python_lint
+
+# TODO(pwbug/454) Remove after downstream projects switch to using functions
+# directly.
+_LINT_CHECKS = (gn_python_lint, )
_ALL_CHECKS = (gn_python_check, )