From b14aab857c0dedfa51ad0abf7bb50bcc866e54a8 Mon Sep 17 00:00:00 2001 From: LightArrowsEXE Date: Wed, 4 Sep 2024 23:48:29 +0200 Subject: [PATCH] SPath: Implement more helper methods --- stgpytools/types/file.py | 53 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/stgpytools/types/file.py b/stgpytools/types/file.py index 7411081..5c8ebb2 100644 --- a/stgpytools/types/file.py +++ b/stgpytools/types/file.py @@ -1,9 +1,12 @@ from __future__ import annotations +import shutil from os import PathLike, listdir, path from pathlib import Path from typing import TYPE_CHECKING, Any, Callable, Iterable, Literal, TypeAlias, Union +from ..exceptions import FileWasNotFoundError + __all__ = [ 'FilePathType', 'FileDescriptor', 'FileOpener', @@ -121,6 +124,8 @@ def append_to_stem(self, suffixes: str | Iterable[str], sep: str = '_') -> SPath return self.with_stem(sep.join([self.stem, *to_arr(suffixes)])) # type:ignore[list-item] def move_dir(self, dst: SPath, *, mode: int = 0o777) -> None: + """Move the directory to the destination.""" + dst.mkdir(mode, True, True) for file in listdir(self): @@ -134,5 +139,53 @@ def move_dir(self, dst: SPath, *, mode: int = 0o777) -> None: self.rmdir() + def lglob(self, pattern: str) -> list[SPath]: + """Glob the path and return the list of paths.""" + + return [SPath(p) for p in self.glob(pattern)] + + def find_newest_file(self, pattern: str = '*') -> SPath | None: + """Find the most recently modified file matching the given pattern in the directory.""" + + matching_files = self.glob(pattern) + + if not matching_files: + return None + + return max(matching_files, key=lambda p: p.stat().st_mtime, default=None) # type:ignore + + def is_empty_dir(self) -> bool: + """Check if the directory is empty.""" + + return self.is_dir() and not any(self.iterdir()) + + def get_size(self) -> int: + """Get the size of the file or directory in bytes.""" + + if self.is_file(): + return self.stat().st_size + elif self.is_dir(): + return sum(f.stat().st_size for f in self.rglob('*') if f.is_file()) + + raise FileWasNotFoundError("Path is neither a file nor a directory") + + def find_files_by_extension(self, extension: str) -> list[SPath]: + """Find all files with the given extension in the directory and its subdirectories.""" + + return [SPath(p) for p in self.rglob(f'*.{extension.lstrip('.')}')] + + def backup(self, dst: SPath | None = None) -> SPath: + """Create a backup of the file or directory.""" + + backup_path = dst if dst else self.with_suffix(f'{self.suffix}_backup') + backup_path.mkdirp() + + if self.is_file(): + shutil.copy2(self, backup_path) + elif self.is_dir(): + shutil.copytree(self, backup_path) + + return SPath(backup_path) + SPathLike = Union[str, Path, SPath]