Refactor shared script helpers

This commit is contained in:
ajp_anton
2026-06-08 02:55:31 +00:00
parent ae05bc9710
commit b6024d8b65
10 changed files with 855 additions and 356 deletions
+150
View File
@@ -0,0 +1,150 @@
from __future__ import annotations
import os
from pathlib import Path
def existing_input_paths(args: list[str], *, print_missing: bool = False) -> list[Path]:
paths: list[Path] = []
for arg in args:
path = Path(arg).expanduser()
if path.exists():
paths.append(path.resolve())
elif print_missing:
print(f"Skipping missing path: {arg}")
return paths
def determine_working_dir(args: list[str]) -> Path:
paths = existing_input_paths(args)
if not paths:
raise RuntimeError("No existing input paths were provided.")
if len(paths) == 1:
path = paths[0]
return path if path.is_dir() else path.parent
try:
common = Path(os.path.commonpath([str(path) for path in paths]))
except ValueError as exc:
raise RuntimeError("Input paths do not have a common ancestor.") from exc
if str(common) == common.anchor:
raise RuntimeError("Input paths only share the filesystem root; choose items closer together.")
return common
def collect_files(
args: list[str],
*,
allowed_exts: set[str] | None = None,
print_missing: bool = False,
sort_by_name: bool = False,
) -> list[Path]:
files: list[Path] = []
seen: set[Path] = set()
for arg in args:
path = Path(arg).expanduser()
if path.is_file():
candidates = [path] if allowed_exts is None or path.suffix.lower() in allowed_exts else []
elif path.is_dir():
candidates = [
child
for child in path.rglob("*")
if child.is_file()
and (allowed_exts is None or child.suffix.lower() in allowed_exts)
]
else:
if print_missing:
print(f"Skipping missing path: {arg}")
continue
for candidate in candidates:
resolved = candidate.resolve()
if resolved not in seen:
seen.add(resolved)
files.append(resolved)
if sort_by_name:
return sorted(files, key=lambda path: path.name.lower())
return sorted(files, key=lambda path: str(path).lower())
def first_file_from_path(path: Path) -> Path | None:
path = path.expanduser()
if path.is_file():
return path.resolve()
if path.is_dir():
for child in sorted(path.rglob("*"), key=lambda item: str(item).lower()):
if child.is_file():
return child.resolve()
return None
def cleanup_roots_from_args(args: list[str]) -> list[Path]:
roots: list[Path] = []
seen: set[Path] = set()
for arg in args:
path = Path(arg).expanduser()
if path.is_dir():
resolved = path.resolve()
if resolved not in seen:
seen.add(resolved)
roots.append(resolved)
return roots
def unique_path(path: Path) -> Path:
if not path.exists():
return path
for index in range(1, 10000):
candidate = path.with_name(f"{path.stem}-{index}{path.suffix}")
if not candidate.exists():
return candidate
raise RuntimeError(f"Could not find an available name for {path}")
def unique_existing_target(path: Path, planned_sources: set[Path]) -> Path:
if not path.exists() or path.resolve() in planned_sources:
return path
for index in range(1, 10000):
candidate = path.with_name(f"{path.stem}-{index}{path.suffix}")
if not candidate.exists() or candidate.resolve() in planned_sources:
return candidate
raise RuntimeError(f"Could not find available target name for {path}")
def remove_empty_dirs(cleanup_roots: list[Path], keep_dirs: set[Path]) -> list[Path]:
removed: list[Path] = []
candidates: set[Path] = set()
for root in cleanup_roots:
if not root.exists() or not root.is_dir():
continue
for directory, subdirs, _files in os.walk(root, topdown=False):
path = Path(directory).resolve()
candidates.add(path)
for subdir in subdirs:
child = (Path(directory) / subdir).resolve()
if child.exists() and child.is_dir():
candidates.add(child)
for directory in sorted(candidates, key=lambda path: len(path.parts), reverse=True):
if directory in keep_dirs:
continue
try:
directory.rmdir()
removed.append(directory)
except OSError:
pass
return removed
def display_path_from_working_dir(path: Path, working_dir: Path) -> str:
try:
relative = path.resolve().relative_to(working_dir.resolve())
except ValueError:
return str(path)
return "." if str(relative) == "." else str(relative)