Source code for scfile.utils.files
"""Basic file and path operations."""
import os
import sys
from pathlib import Path
from scfile import types
from scfile.consts import ALLOWED_SUFFIXES
[docs]
def resource(
path: types.PathLike,
) -> types.Path:
"""Resolve resource path, accounting for MEIPASS environment variable."""
meipass = getattr(sys, "_MEIPASS", None)
if meipass:
return Path(meipass) / path
root = Path(__file__).parent.parent.absolute()
gui = root / "gui"
return gui / path
[docs]
def resolve(
sources: types.FilesSources,
) -> types.FilesPaths:
"""Normalize paths into a clean minimal set."""
paths = list(map(Path, sources))
resolved = sorted({path.resolve() for path in paths if path.exists()})
unique: types.FilesPaths = []
for path in resolved:
if not any(path.is_relative_to(parent) for parent in unique):
unique.append(path)
return unique
[docs]
def walk(
sources: types.FilesSources,
whitelist: types.FilesWhitelist | None = None,
parent: bool = False,
) -> types.FilesWalk:
"""Walk through files in given sources, optionally filtering by whitelist."""
paths = resolve(sources)
paths = list(map(str, paths))
whitelist = tuple(whitelist or ALLOWED_SUFFIXES)
for root in paths:
base = os.path.dirname(root) if parent else root
if os.path.isfile(root):
if root.lower().endswith(whitelist):
yield types.FileEntry(
root=root,
path=root,
relpath=os.path.relpath(root, base),
)
continue
stack = [root]
while stack:
current = stack.pop()
try:
with os.scandir(current) as it:
for entry in it:
if entry.is_dir():
stack.append(entry.path)
elif entry.is_file():
if entry.name.lower().endswith(whitelist):
yield types.FileEntry(
root=root,
path=entry.path,
relpath=os.path.relpath(entry.path, base),
)
except PermissionError:
continue
[docs]
def destination(
relpath: str,
relative: bool,
output: str | None,
) -> str | None:
"""Resolve destination path based on options."""
if relative and output:
return os.path.join(output, os.path.dirname(relpath))
return output