1111
1212import os
1313import subprocess
14- from dataclasses import dataclass
14+ from dataclasses import dataclass , fields
1515from pathlib import Path
1616
1717TYPE_CHECKING = False
5252MACOS_DIRS = frozenset ({"Mac" })
5353WASI_DIRS = frozenset ({Path ("Tools" , "wasm" )})
5454
55+ LIBRARY_FUZZER_PATHS = frozenset ({
56+ # All C/CPP fuzzers.
57+ Path ("configure" ),
58+ Path (".github/workflows/reusable-cifuzz.yml" ),
59+ # ast
60+ Path ("Lib/ast.py" ),
61+ Path ("Python/ast.c" ),
62+ # configparser
63+ Path ("Lib/configparser.py" ),
64+ # csv
65+ Path ("Lib/csv.py" ),
66+ Path ("Modules/_csv.c" ),
67+ # decode
68+ Path ("Lib/encodings/" ),
69+ Path ("Modules/_codecsmodule.c" ),
70+ Path ("Modules/cjkcodecs/" ),
71+ Path ("Modules/unicodedata*" ),
72+ # difflib
73+ Path ("Lib/difflib.py" ),
74+ # email
75+ Path ("Lib/email/" ),
76+ # html
77+ Path ("Lib/html/" ),
78+ Path ("Lib/_markupbase.py" ),
79+ # http.client
80+ Path ("Lib/http/client.py" ),
81+ # json
82+ Path ("Lib/json/" ),
83+ Path ("Modules/_json.c" ),
84+ # plist
85+ Path ("Lib/plistlib.py" ),
86+ # re
87+ Path ("Lib/re/" ),
88+ Path ("Modules/_sre/" ),
89+ # tarfile
90+ Path ("Lib/tarfile.py" ),
91+ # tomllib
92+ Path ("Modules/tomllib/" ),
93+ # xml
94+ Path ("Lib/xml/" ),
95+ Path ("Lib/_markupbase.py" ),
96+ Path ("Modules/expat/" ),
97+ Path ("Modules/pyexpat.c" ),
98+ # zipfile
99+ Path ("Lib/zipfile/" ),
100+ })
101+
55102
56103@dataclass (kw_only = True , slots = True )
57104class Outputs :
58105 run_android : bool = False
59106 run_ci_fuzz : bool = False
107+ run_ci_fuzz_stdlib : bool = False
60108 run_docs : bool = False
61109 run_ios : bool = False
62110 run_macos : bool = False
@@ -96,6 +144,11 @@ def compute_changes() -> None:
96144 else :
97145 print ("Branch too old for CIFuzz tests; or no C files were changed" )
98146
147+ if outputs .run_ci_fuzz_stdlib :
148+ print ("Run CIFuzz tests for stdlib" )
149+ else :
150+ print ("Branch too old for CIFuzz tests; or no stdlib files were changed" )
151+
99152 if outputs .run_docs :
100153 print ("Build documentation" )
101154
@@ -146,9 +199,18 @@ def get_file_platform(file: Path) -> str | None:
146199 return None
147200
148201
202+ def is_fuzzable_library_file (file : Path ) -> bool :
203+ return any (
204+ (file .is_relative_to (needs_fuzz ) and needs_fuzz .is_dir ())
205+ or (file == needs_fuzz and file .is_file ())
206+ for needs_fuzz in LIBRARY_FUZZER_PATHS
207+ )
208+
209+
149210def process_changed_files (changed_files : Set [Path ]) -> Outputs :
150211 run_tests = False
151212 run_ci_fuzz = False
213+ run_ci_fuzz_stdlib = False
152214 run_docs = False
153215 run_windows_tests = False
154216 run_windows_msi = False
@@ -162,8 +224,8 @@ def process_changed_files(changed_files: Set[Path]) -> Outputs:
162224 doc_file = file .suffix in SUFFIXES_DOCUMENTATION or doc_or_misc
163225
164226 if file .parent == GITHUB_WORKFLOWS_PATH :
165- if file .name == "build.yml" :
166- run_tests = run_ci_fuzz = True
227+ if file .name in ( "build.yml" , "reusable-cifuzz.yml" ) :
228+ run_tests = run_ci_fuzz = run_ci_fuzz_stdlib = True
167229 has_platform_specific_change = False
168230 if file .name == "reusable-docs.yml" :
169231 run_docs = True
@@ -194,6 +256,8 @@ def process_changed_files(changed_files: Set[Path]) -> Outputs:
194256 ("Modules" , "_xxtestfuzz" ),
195257 }:
196258 run_ci_fuzz = True
259+ if not run_ci_fuzz_stdlib and is_fuzzable_library_file (file ):
260+ run_ci_fuzz_stdlib = True
197261
198262 # Check for changed documentation-related files
199263 if doc_file :
@@ -227,6 +291,7 @@ def process_changed_files(changed_files: Set[Path]) -> Outputs:
227291 return Outputs (
228292 run_android = run_android ,
229293 run_ci_fuzz = run_ci_fuzz ,
294+ run_ci_fuzz_stdlib = run_ci_fuzz_stdlib ,
230295 run_docs = run_docs ,
231296 run_ios = run_ios ,
232297 run_macos = run_macos ,
@@ -261,16 +326,10 @@ def write_github_output(outputs: Outputs) -> None:
261326 return
262327
263328 with open (os .environ ["GITHUB_OUTPUT" ], "a" , encoding = "utf-8" ) as f :
264- f .write (f"run-android={ bool_lower (outputs .run_android )} \n " )
265- f .write (f"run-ci-fuzz={ bool_lower (outputs .run_ci_fuzz )} \n " )
266- f .write (f"run-docs={ bool_lower (outputs .run_docs )} \n " )
267- f .write (f"run-ios={ bool_lower (outputs .run_ios )} \n " )
268- f .write (f"run-macos={ bool_lower (outputs .run_macos )} \n " )
269- f .write (f"run-tests={ bool_lower (outputs .run_tests )} \n " )
270- f .write (f"run-ubuntu={ bool_lower (outputs .run_ubuntu )} \n " )
271- f .write (f"run-wasi={ bool_lower (outputs .run_wasi )} \n " )
272- f .write (f"run-windows-msi={ bool_lower (outputs .run_windows_msi )} \n " )
273- f .write (f"run-windows-tests={ bool_lower (outputs .run_windows_tests )} \n " )
329+ for field in fields (outputs ):
330+ name = field .name .replace ("_" , "-" )
331+ val = bool_lower (getattr (outputs , field .name ))
332+ f .write (f"{ name } ={ val } \n " )
274333
275334
276335def bool_lower (value : bool , / ) -> str :
0 commit comments