pw_presubmit: Path filtering; callable _Check

- Use re.search to filter paths instead of re.fullmatch. ^ and $ can be
  used to match the whole string.
- Filter paths using Path.as_posix() so that they are the same across
  Windows, macOS, and Linux.
- Make _Check objects callable. That makes it possible to reuse
  functions with @filter_paths and apply different filters.

Change-Id: I66e020aabe66c3f3ff50f167f54b28e2949b276f
diff --git a/pw_presubmit/py/pw_presubmit/tools.py b/pw_presubmit/py/pw_presubmit/tools.py
index 95a9639..c4c9ee1 100644
--- a/pw_presubmit/py/pw_presubmit/tools.py
+++ b/pw_presubmit/py/pw_presubmit/tools.py
@@ -517,14 +517,15 @@
         paths: Sequence[Path]) -> Dict['_Check', Sequence[Path]]:
     checks_to_paths: Dict[_Check, Sequence[Path]] = {}
 
+    str_paths = [path.as_posix() for path in paths]
+
     for filt, checks in filter_to_checks.items():
         exclude = [re.compile(exp) for exp in filt.exclude]
 
         filtered_paths = tuple(
-            path for path in paths
-            if any(str(path).endswith(end)
-                   for end in filt.endswith) and not any(
-                       exp.fullmatch(str(path)) for exp in exclude))
+            Path(path) for path in str_paths
+            if any(path.endswith(end) for end in filt.endswith) and not any(
+                exp.search(path) for exp in exclude))
 
         for check in checks:
             if filtered_paths or check.always_run:
@@ -674,6 +675,14 @@
 
         return _Result.PASS
 
+    def __call__(self, ctx: PresubmitContext, *args, **kwargs):
+        """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.
+      """
+        return self._check(ctx, *args, **kwargs)
+
 
 def _ensure_is_valid_presubmit_check_function(check: Callable) -> None:
     """Checks if a Callable can be used as a presubmit check."""
@@ -702,6 +711,11 @@
                  always_run: bool = False):
     """Decorator for filtering the paths list for a presubmit check function.
 
+    Path filters only apply when the function is used as a presubmit check.
+    Filters are ignored when the functions are called directly. This makes it
+    possible to reuse functions wrapped in @filter_paths in other presubmit
+    checks, potentially with different path filtering rules.
+
     Args:
         endswith: str or iterable of path endings to include
         exclude: regular expressions of paths to exclude