This is an automated email from the ASF dual-hosted git repository.
nchammas pushed a commit to branch branch-4.x
in repository https://gitbox.apache.org/repos/asf/spark.git
The following commit(s) were added to refs/heads/branch-4.x by this push:
new 8cc56ea6006b [SPARK-58885][BUILD][INFRA] Refactor handling of ignored
patterns in module test detection
8cc56ea6006b is described below
commit 8cc56ea6006b4ea182df5ed4713ba345ea8e5e74
Author: Nicholas Chammas <[email protected]>
AuthorDate: Fri Aug 21 10:46:20 2026 -0400
[SPARK-58885][BUILD][INFRA] Refactor handling of ignored patterns in module
test detection
Refactor how we ignore file patterns when determining what modules to test.
Use `pathlib.PurePath` glob-style patterns instead of regexes.
Add some doctests to `modules.py` so we can test the new `is_ignored_file`
function directly. Ensure these doctests can be run by themselves and are also
run as part of `run-tests.py`.
[1]:
https://github.com/apache/spark/actions/runs/32265494187/job/96146278679
There is also a problem with using regexes for these ignore patterns
because most of the patterns include extensions like `.sh` or `.md` without
escaping the `.`. This leads to the unlikely but nonetheless surprising case of
`README1md` matching the `README.md` pattern.
No.
I ran the doctests on `modules.py` and `utils.py`.
I wrote this with assistance from GitHub Copilot.
Closes #58136 from nchammas/SPARK-58885-ignored-test-patterns.
Authored-by: Nicholas Chammas <[email protected]>
Signed-off-by: Nicholas Chammas <[email protected]>
(cherry picked from commit 9a75a1d04be4b82c0626f5ac00cb65b59cfdf37e)
Signed-off-by: Nicholas Chammas <[email protected]>
---
dev/run-tests.py | 13 ++--
dev/sparktestsupport/modules.py | 131 ++++++++++++++++++++++++++++------------
dev/sparktestsupport/utils.py | 4 ++
3 files changed, 103 insertions(+), 45 deletions(-)
diff --git a/dev/run-tests.py b/dev/run-tests.py
index 948f92a9ef4c..1c323ed1b545 100755
--- a/dev/run-tests.py
+++ b/dev/run-tests.py
@@ -640,10 +640,7 @@ def main():
# If APACHE_SPARK_REF is set, we are in a forked repository.
# Otherwise if we have a list of changed files, we must be in
post-merge CI.
if not os.environ.get("APACHE_SPARK_REF", "") and changed_files:
- # Filter out all dev_tools files because they are not relevant
- relevant_changed_files = [
- f for f in changed_files if not
modules.dev_tools.contains_file(f)
- ]
+ relevant_changed_files = [f for f in changed_files if not
modules.is_ignored_file(f)]
# If there are relevant changed files that are not pyspark, we
don't do smart test
if any(
not (f.endswith(".py") and f.startswith("python/pyspark/"))
@@ -668,9 +665,15 @@ def main():
def _test():
import doctest
+ import sparktestsupport.modules
import sparktestsupport.utils
- failure_count = doctest.testmod(sparktestsupport.utils)[0] +
doctest.testmod()[0]
+ test_results = (
+ doctest.testmod(sparktestsupport.modules),
+ doctest.testmod(sparktestsupport.utils),
+ doctest.testmod(),
+ )
+ failure_count = sum([num_failures for (num_failures, num_tests) in
test_results])
if failure_count:
sys.exit(-1)
diff --git a/dev/sparktestsupport/modules.py b/dev/sparktestsupport/modules.py
index 425879ea48e8..f80cde17b93a 100644
--- a/dev/sparktestsupport/modules.py
+++ b/dev/sparktestsupport/modules.py
@@ -19,10 +19,88 @@ from functools import total_ordering
import itertools
import os
import re
-from pathlib import Path
+import sys
+from pathlib import Path, PurePath
all_modules = []
+# These are `pathlib.PurePath` glob-style patterns with some customization:
+# - Bare patterns match a file name at any depth.
+# - Leading slashes anchor at the repository root.
+# - A trailing slash denotes an ignored directory subtree.
+# See: https://docs.python.org/3/library/pathlib.html#pathlib.PurePath.match
+#
+# Rejected alternatives:
+# - Regexes present a footgun in that `.` needs to be carefully escaped but
+# often isn't.
+# - `.gitignore`-style patterns would be ideal but don't have support in the
+# standard library.
+ignored_file_patterns = (
+ ".asf.yaml",
+ ".gitignore",
+ "AGENTS.md",
+ "CONTRIBUTING.md",
+ "README.md",
+ "/LICENSE-binary",
+ "/NOTICE-binary",
+ "/scalastyle-config.xml",
+ "/SECURITY.md",
+ "/dev/checkstyle-suppressions.xml",
+ "/dev/checkstyle.xml",
+ "/dev/create_jira_and_branch.py",
+ "/dev/create_spark_jira.py",
+ "/dev/create-release/",
+ "/dev/lint-python",
+ "/dev/lint-scala",
+ "/dev/make-distribution.sh",
+ "/dev/merge_spark_pr.py",
+ "/dev/pr_merge_status.py",
+ "/dev/reformat-python",
+ "/dev/requirements.txt",
+ "/dev/spark_merge_footer.py",
+ "/dev/spark-test-image/lint/Dockerfile",
+ "/dev/structured_logging_style.py",
+ "/ui-test/package-lock.json",
+ "/ui-test/package.json",
+)
+
+
+def is_ignored_file(filename: str) -> bool:
+ """
+ Return whether a repository-relative path should be ignored when selecting
+ test modules.
+
+ Bare patterns match a file name at any depth:
+ >>> is_ignored_file("python/README.md")
+ True
+
+ Leading slashes anchor at the repository root:
+ >>> is_ignored_file("SECURITY.md")
+ True
+ >>> is_ignored_file("docs/SECURITY.md")
+ False
+
+ A trailing slash ignores a directory subtree:
+ >>> is_ignored_file("dev/create-release/spark-rm/Dockerfile")
+ True
+
+ Non-matches fall through:
+ >>> is_ignored_file("xasfZyaml")
+ False
+ """
+ path = PurePath("/") / filename
+ for pattern in ignored_file_patterns:
+ # TODO: When Python 3.13 becomes the minimum supported version, migrate
+ # to `PurePath.full_match` and use `**` patterns instead of this custom
+ # trailing slash behavior.
+ if pattern.endswith("/"):
+ ignored_directory = PurePath(pattern)
+ if path == ignored_directory or ignored_directory in path.parents:
+ return True
+ elif path.match(pattern):
+ return True
+ return False
+
@total_ordering
class Module(object):
@@ -1737,45 +1815,6 @@ docker_integration_tests = Module(
test_tags=["org.apache.spark.tags.DockerTest"],
)
-
-# dev_tools is a pseudo module that contains all the dev related files that
-# won't impact the CI build and tests (except for CI which is forced to
-# run anyway).
-# This module is created so modifying files in this module won't trigger any
-# tests to run.
-dev_tools = Module(
- name="dev-tools",
- dependencies=[],
- source_file_regexes=[
- ".*README.md",
- ".*AGENTS.md",
- r".*\.gitignore",
- "CONTRIBUTING.md",
- ".asf.yaml",
- "SECURITY.md",
- "NOTICE-binary",
- "LICENSE-binary",
- "ui-test/package.json",
- "ui-test/package-lock.json",
- "scalastyle-config.xml",
- "dev/checkstyle.xml",
- "dev/checkstyle-suppressions.xml",
- "dev/create_jira_and_branch.py",
- "dev/create_spark_jira.py",
- "dev/spark-test-image/lint/Dockerfile",
- "dev/lint-python",
- "dev/lint-scala",
- "dev/reformat-python",
- "dev/structured_logging_style.py",
- "dev/make-distribution.sh",
- "dev/merge_spark_pr.py",
- "dev/requirements.txt",
- "dev/pr_merge_status.py",
- "dev/create_spark_jira.py",
- "dev/create-release/",
- ],
-)
-
# The root module is a dummy module which is used to run all of the tests.
# No other modules should directly depend on this module.
root = Module(
@@ -1793,3 +1832,15 @@ root = Module(
should_run_r_tests=True,
should_run_build_tests=True,
)
+
+
+def _test():
+ import doctest
+
+ failure_count = doctest.testmod()[0]
+ if failure_count:
+ sys.exit(-1)
+
+
+if __name__ == "__main__":
+ _test()
diff --git a/dev/sparktestsupport/utils.py b/dev/sparktestsupport/utils.py
index 1657aa56da67..39bdeed5e934 100755
--- a/dev/sparktestsupport/utils.py
+++ b/dev/sparktestsupport/utils.py
@@ -39,9 +39,13 @@ def determine_modules_for_files(filenames):
['pyspark-core', 'pyspark-install', 'sql']
>>> [x.name for x in
determine_modules_for_files(["file_not_matched_by_any_subproject"])]
['root']
+ >>> [x.name for x in determine_modules_for_files(["python/README.md"])]
+ []
"""
changed_modules = set()
for filename in filenames:
+ if modules.is_ignored_file(filename):
+ continue
if ("GITHUB_ACTIONS" not in os.environ) and
filename.startswith(".github"):
continue
matched_at_least_one_module = False
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]