11"""This script requires Python 3.10.0 or higher to run."""
22
33import configparser
4- import functools
4+ import fnmatch
55import hashlib
66import logging
7- import operator
87import pathlib
98import re
109import xml .etree .ElementTree as Et
1312alphanumeric_pattern = re .compile (r"(\d+)" )
1413
1514
16- def _compose (f : Callable [[Any ], Any ], g : Callable [[Any ], Any ]) -> Callable [[Any ], Any ]:
17- """Composition of two functions f and g."""
18- return lambda * args , ** kwargs : f (g (* args , ** kwargs ))
19-
20-
21- def _complement (f : Callable [[Any ], bool ]) -> Callable [[Any ], bool ]:
22- """Logical complement of function f."""
23- return _compose (operator .not_ , f )
24-
25-
26- def _identity (f : Callable [[Any ], Any ]) -> Callable [[Any ], Any ]:
27- """Identity function."""
28- return f
29-
30-
31- def _exclude_file (file_names : set [str ], path : pathlib .Path ) -> bool :
32- """Whether to exclude a single file."""
33- return path .name in file_names
15+ def _exclude_file (file_patterns : set [str ], path : pathlib .Path ) -> bool :
16+ """Whether to exclude a single file. Supports glob patterns (e.g., '*.scm')."""
17+ return any (fnmatch .fnmatch (path .name , pattern ) for pattern in file_patterns )
3418
3519
3620def _exclude_directory (directory_names : set [str ], path : pathlib .Path ) -> bool :
@@ -42,6 +26,17 @@ def _exclude_directory(directory_names: set[str], path: pathlib.Path) -> bool:
4226 )
4327
4428
29+ def _parse_list_option (config : configparser .ConfigParser , section : str , option : str ) -> set [str ]:
30+ """Split a comma-separated option into a cleaned set of entries."""
31+ if not config .has_option (section , option ):
32+ return set ()
33+ return {
34+ entry
35+ for entry in map (str .strip , config [section ][option ].split ("," ))
36+ if entry
37+ }
38+
39+
4540def _alphanumeric (key : str ) -> list [int | str ]:
4641 """Natural sorting order for numbers, e.g. 10 follows 9."""
4742 return [
@@ -90,22 +85,21 @@ def create_manifest(version: str | None = None, replace: bool = False) -> None:
9085 )
9186 parts .append (attributes )
9287
93- rules = {
94- "include-files" : (_complement , _exclude_file ),
95- "include-directories" : (_complement , _exclude_directory ),
96- "exclude-files" : (_identity , _exclude_file ),
97- "exclude-directories" : (_identity , _exclude_directory ),
98- }
9988 files : list [dict [str , str ]] = []
10089 for section in config .sections ():
101- exclusion_checks = [
102- modifier (functools .partial (check , set (config [section ][option ].split ("," ))))
103- for option , (modifier , check ) in rules .items ()
104- if config .has_option (section , option )
105- ]
90+ include_files = _parse_list_option (config , section , "include-files" )
91+ include_dirs = _parse_list_option (config , section , "include-directories" )
92+ exclude_files = _parse_list_option (config , section , "exclude-files" )
93+ exclude_dirs = _parse_list_option (config , section , "exclude-directories" )
10694 source = pathlib .Path (config [section ]["path" ])
10795 for path in source .glob ("**/*.*" ):
108- if any (is_excluded (path ) for is_excluded in exclusion_checks ):
96+ if include_files and not _exclude_file (include_files , path ):
97+ continue
98+ if include_dirs and not _exclude_directory (include_dirs , path ):
99+ continue
100+ if exclude_files and _exclude_file (exclude_files , path ):
101+ continue
102+ if exclude_dirs and _exclude_directory (exclude_dirs , path ):
109103 continue
110104 data = path .read_bytes ()
111105 sha1 = hashlib .sha1 (data ).hexdigest ()
0 commit comments