Extensions: ensure paths are always included for the "build" command

Scanning the file-system for paths to include didn't detect cases when
explicitly included paths (such as wheels) were missing.

Change the logic to build a list of paths which is merged with pattern
matched paths (de-duplicating), then add all files into the ZIP.
Any missing paths will raise an error.
This commit is contained in:
Campbell Barton
2024-06-05 14:17:04 +10:00
parent b15dc90c9d
commit 17ec64770c

View File

@@ -2717,13 +2717,19 @@ class subcmd_author:
message_error(msg_fn, "Error parsing TOML \"{:s}\" {:s}".format(pkg_manifest_filepath, error_msg))
return False
# Always include wheels & manifest.
build_paths_extra = (
# Inclusion of the manifest is implicit.
# No need to require the manifest to include itself.
PKG_MANIFEST_FILENAME_TOML,
*(manifest.wheels or ()),
)
if (manifest_build_data := manifest_data.get("build")) is not None:
manifest_build_test = PkgManifest_Build.from_dict_all_errors(manifest_build_data, extra_paths=[
# Inclusion of the manifest is implicit.
# No need to require the manifest to include itself.
PKG_MANIFEST_FILENAME_TOML,
*(manifest.wheels or ()),
])
manifest_build_test = PkgManifest_Build.from_dict_all_errors(
manifest_build_data,
extra_paths=build_paths_extra,
)
if isinstance(manifest_build_test, list):
for error_msg in manifest_build_test:
message_error(msg_fn, "Error parsing TOML \"{:s}\" {:s}".format(pkg_manifest_filepath, error_msg))
@@ -2746,23 +2752,73 @@ class subcmd_author:
if manifest_build.paths_exclude_pattern is not None:
build_paths_exclude_pattern = PathPatternMatch(manifest_build.paths_exclude_pattern)
build_paths: Optional[List[str]] = None
build_paths: List[Tuple[str, str]] = []
# Manifest & wheels.
if build_paths_extra:
build_paths.extend(build_paths_expand_iter(pkg_source_dir, build_paths_extra))
if manifest_build.paths is not None:
build_paths = manifest_build.paths
build_paths.extend(build_paths_expand_iter(pkg_source_dir, manifest_build.paths))
else:
# Mixing literal and pattern matched lists of files is a hassle.
# De-duplicate canonical root-relative path names.
def filepath_canonical_from_relative(filepath_rel: str):
filepath_rel = os.path.normpath(filepath_rel)
if os.sep == "\\":
filepath_rel = filepath_rel.replace("\\", "/")
return filepath_rel
def scandir_filter_with_paths_exclude_pattern(filepath: str, is_dir: bool) -> bool:
assert build_paths_exclude_pattern is not None
if os.sep == "\\":
filepath = filepath.replace("\\", "/")
if is_dir:
assert not filepath.endswith("/")
filepath = filepath + "/"
assert not filepath.startswith(("/", "./", "../"))
return not build_paths_exclude_pattern.test_path(filepath)
# Use lowercase to prevent duplicates on MS-Windows.
build_paths_extra_canonical: Set[int] = set(
filepath_canonical_from_relative(f).lower()
for f in build_paths_extra
)
def scandir_filter_fallback(filepath: str, is_dir: bool) -> bool:
_ = is_dir
return not os.path.basename(filepath).startswith(".")
# Scanning the file-system may fail, surround by try/except.
try:
if build_paths_exclude_pattern:
def scandir_filter_with_paths_exclude_pattern(filepath: str, is_dir: bool) -> bool:
# Returning true includes the file.
assert build_paths_exclude_pattern is not None
if os.sep == "\\":
filepath = filepath.replace("\\", "/")
filepath_canonical = filepath
if is_dir:
assert not filepath.endswith("/")
filepath = filepath + "/"
assert not filepath.startswith(("/", "./", "../"))
result = not build_paths_exclude_pattern.test_path(filepath)
if result and (not is_dir):
# Finally check the path isn't one of the known paths.
if filepath_canonical.lower() in build_paths_extra_canonical:
result = False
return result
build_paths.extend(
scandir_recursive(
pkg_source_dir,
filter_fn=scandir_filter_with_paths_exclude_pattern,
),
)
else:
# In this case there isn't really a good option, just ignore all dot-files.
def scandir_filter_fallback(filepath: str, is_dir: bool) -> bool:
# Returning true includes the file.
result = not os.path.basename(filepath).startswith(".")
if result and (not is_dir):
# Finally check the path isn't
if filepath_canonical.lower() in build_paths_extra_canonical:
result = False
return result
build_paths.extend(scandir_recursive(pkg_source_dir, filter_fn=scandir_filter_fallback))
del build_paths_extra_canonical
except Exception as ex:
message_status(msg_fn, "Error building path list \"{:s}\"".format(str(ex)))
return False
pkg_filename = manifest.id + PKG_EXT
@@ -2797,20 +2853,7 @@ class subcmd_author:
return False
with contextlib.closing(zip_fh_context) as zip_fh:
if build_paths is not None:
filepath_iterator = build_paths_expand_iter(pkg_source_dir, build_paths)
else:
filepath_iterator = scandir_recursive(
pkg_source_dir,
filter_fn=(
scandir_filter_with_paths_exclude_pattern if build_paths_exclude_pattern else
# In this case there isn't really a good option, just ignore all dot-files.
scandir_filter_fallback
),
)
for filepath_abs, filepath_rel in filepath_iterator:
for filepath_abs, filepath_rel in build_paths:
if filepath_rel in filenames_root_exclude:
continue