From 18f3deda12d4f566b6f9c1f35a55a6d318cc9a6b Mon Sep 17 00:00:00 2001 From: Fabio Caccamo Date: Tue, 19 Mar 2024 15:01:15 +0100 Subject: [PATCH] Add `transform_filepath` method. #12 #13 --- README.md | 8 +++++ fsutil/__init__.py | 57 ++++++++++++++++++++++++++++++++++ tests/test.py | 77 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+) diff --git a/README.md b/README.md index 4488023..296afdc 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,7 @@ import fsutil - [`split_filename`](#split_filename) - [`split_filepath`](#split_filepath) - [`split_path`](#split_path) +- [`transform_filepath`](#transform_filepath) - [`write_file`](#write_file) - [`write_file_json`](#write_file_json) @@ -726,6 +727,13 @@ dirpath, filename = fsutil.split_filepath(path) path_names = fsutil.split_path(path) ``` +#### `transform_filepath` + +```python +# Trasform a filepath by applying the provided optional changes. +filepath = fsutil.transform_filepath(path, dirpath=None, basename=lambda b: slugify(b), extension="webp") +``` + #### `write_file` ```python diff --git a/fsutil/__init__.py b/fsutil/__init__.py index bd55034..400c9db 100644 --- a/fsutil/__init__.py +++ b/fsutil/__init__.py @@ -1339,6 +1339,63 @@ def split_path(path: PathIn) -> list[str]: return names +def transform_filepath( + path: PathIn, + *, + dirpath: str | Callable[[str], str] | None = None, + basename: str | Callable[[str], str] | None = None, + extension: str | Callable[[str], str] | None = None, +) -> str: + """ + Trasform a filepath by applying the provided optional changes. + + :param path: The path. + :type path: PathIn + :param dirpath: The new dirpath or a callable. + :type dirpath: str | Callable[[str], str] | None + :param basename: The new basename or a callable. + :type basename: str | Callable[[str], str] | None + :param extension: The new extension or a callable. + :type extension: str | Callable[[str], str] | None + + :returns: The filepath with the applied changes. + :rtype: str + """ + + def _get_value( + new_value: str | Callable[[str], str] | None, + old_value: str, + ) -> str: + value = old_value + if new_value is not None: + if callable(new_value): + value = new_value(old_value) + elif isinstance(new_value, str): + value = new_value + else: + value = old_value + return value + + if all([dirpath is None, basename is None, extension is None]): + raise ValueError( + "Invalid arguments: at least one of " + "'dirpath', 'basename' or 'extension' is required." + ) + old_dirpath, old_filename = split_filepath(path) + old_basename, old_extension = split_filename(old_filename) + new_dirpath = _get_value(dirpath, old_dirpath) + new_basename = _get_value(basename, old_basename) + new_extension = _get_value(extension, old_extension) + if not any([new_dirpath, new_basename, new_extension]): + raise ValueError( + "Invalid arguments: at least one of " + "'dirpath', 'basename' or 'extension' is required." + ) + new_filename = join_filename(new_basename, new_extension) + new_filepath = join_filepath(new_dirpath, new_filename) + return new_filepath + + def _write_file_atomic( path: PathIn, content: str, diff --git a/tests/test.py b/tests/test.py index 0ccf9ee..869ef9c 100644 --- a/tests/test.py +++ b/tests/test.py @@ -1222,6 +1222,83 @@ def test_split_path(self): s = "/root/a/b/c/Document.txt" self.assertEqual(fsutil.split_path(s), ["root", "a", "b", "c", "Document.txt"]) + def test_transform_filepath_without_args(self): + s = "/root/a/b/c/Document.txt" + with self.assertRaises(ValueError): + (fsutil.transform_filepath(s),) + + def test_transform_filepath_with_empty_str_args(self): + s = "/root/a/b/c/Document.txt" + self.assertEqual( + fsutil.transform_filepath(s, dirpath=""), + "Document.txt", + ) + self.assertEqual( + fsutil.transform_filepath(s, basename=""), + "/root/a/b/c/txt", + ) + self.assertEqual( + fsutil.transform_filepath(s, extension=""), + "/root/a/b/c/Document", + ) + self.assertEqual( + fsutil.transform_filepath( + s, dirpath="/root/x/y/z/", basename="NewDocument", extension="xls" + ), + "/root/x/y/z/NewDocument.xls", + ) + with self.assertRaises(ValueError): + (fsutil.transform_filepath(s, dirpath="", basename="", extension=""),) + + def test_transform_filepath_with_str_args(self): + s = "/root/a/b/c/Document.txt" + self.assertEqual( + fsutil.transform_filepath(s, dirpath="/root/x/y/z/"), + "/root/x/y/z/Document.txt", + ) + self.assertEqual( + fsutil.transform_filepath(s, basename="NewDocument"), + "/root/a/b/c/NewDocument.txt", + ) + self.assertEqual( + fsutil.transform_filepath(s, extension="xls"), + "/root/a/b/c/Document.xls", + ) + self.assertEqual( + fsutil.transform_filepath(s, extension=".xls"), + "/root/a/b/c/Document.xls", + ) + self.assertEqual( + fsutil.transform_filepath( + s, dirpath="/root/x/y/z/", basename="NewDocument", extension="xls" + ), + "/root/x/y/z/NewDocument.xls", + ) + + def test_transform_filepath_with_callable_args(self): + s = "/root/a/b/c/Document.txt" + self.assertEqual( + fsutil.transform_filepath(s, dirpath=lambda d: f"{d}/x/y/z/"), + "/root/a/b/c/x/y/z/Document.txt", + ) + self.assertEqual( + fsutil.transform_filepath(s, basename=lambda b: b.lower()), + "/root/a/b/c/document.txt", + ) + self.assertEqual( + fsutil.transform_filepath(s, extension=lambda e: "xls"), + "/root/a/b/c/Document.xls", + ) + self.assertEqual( + fsutil.transform_filepath( + s, + dirpath=lambda d: f"{d}/x/y/z/", + basename=lambda b: b.lower(), + extension=lambda e: "xls", + ), + "/root/a/b/c/x/y/z/document.xls", + ) + def test_write_file(self): path = self.temp_path("a/b/c.txt") fsutil.write_file(path, content="Hello World")