diff --git a/test/trackpack_test.py b/test/trackpack_test.py index 3fded61..b907cf4 100644 --- a/test/trackpack_test.py +++ b/test/trackpack_test.py @@ -15,8 +15,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import os import unittest +from pathlib import Path from unittest.mock import call, patch from trackpack.trackpacker import MissingFileException, TrackPacker @@ -25,6 +25,8 @@ class TestTrackPack(unittest.TestCase): @patch("os.walk") def test_discover_audiofiles_returns_audio_files(self, walk_mock) -> None: + export_dir = Path("/tmp/export") + walk_mock.return_value = _create_walk_files( [ "proj stem2.wav", @@ -35,13 +37,13 @@ def test_discover_audiofiles_returns_audio_files(self, walk_mock) -> None: ] ) - trackpacker = TrackPacker("proj", "/tmp/export") + trackpacker = TrackPacker("proj", str(export_dir)) (master, stems) = trackpacker.discover_audiofiles() - walk_mock.assert_called_with("/tmp/export") + walk_mock.assert_called_with(str(export_dir)) self.assertEqual("proj.wav", master) self.assertListEqual( _files_in_dir( - "/tmp/export", + export_dir, [ "proj stem2.wav", "proj stem4.wav", @@ -56,6 +58,7 @@ def test_discover_audiofiles_returns_audio_files(self, walk_mock) -> None: def test_discover_audiofiles_returns_only_related_audio_files( self, walk_mock ) -> None: + export_dir = Path("/tmp/export") walk_mock.return_value = _create_walk_files( [ "proj stem2.wav", @@ -67,16 +70,17 @@ def test_discover_audiofiles_returns_only_related_audio_files( ] ) - trackpacker = TrackPacker("proj", "/tmp/export") + trackpacker = TrackPacker("proj", str(export_dir)) (_, stems) = trackpacker.discover_audiofiles() self.assertListEqual( - _files_in_dir("/tmp/export/", ["proj stem2.wav", "proj stem1.wav"]), stems + _files_in_dir(export_dir, ["proj stem2.wav", "proj stem1.wav"]), stems ) @patch("os.walk") def test_discover_audiofiles_master_track_matches_project_name( self, walk_mock ) -> None: + export_dir = Path("/tmp/export") walk_mock.return_value = _create_walk_files( [ "example.wav", @@ -86,32 +90,37 @@ def test_discover_audiofiles_master_track_matches_project_name( "proj stem3.wav", ] ) - trackpacker = TrackPacker("example", "/tmp/export") + trackpacker = TrackPacker("example", str(export_dir)) (master, _) = trackpacker.discover_audiofiles() self.assertEqual("example.wav", master) @patch("os.walk") def test_discover_audiofiles_fails_if_no_master(self, walk_mock) -> None: + export_dir = Path("/tmp/export") walk_mock.return_value = _create_walk_files( ["proj stem1.wav", "proj stem2.wav"] ) with self.assertRaises(MissingFileException): - trackpacker = TrackPacker("proj", "/tmp/export") + trackpacker = TrackPacker("proj", str(export_dir)) trackpacker.discover_audiofiles() @patch("os.walk") def test_discover_audiofiles_fails_if_no_stems(self, walk_mock) -> None: + export_dir = Path("/tmp/export") walk_mock.return_value = _create_walk_files(["proj.wav"]) with self.assertRaises(MissingFileException): - trackpacker = TrackPacker("proj", "/tmp/export") + trackpacker = TrackPacker("proj", str(export_dir)) trackpacker.discover_audiofiles() @patch("os.walk") def test_discover_audiofiles_returns_explicit_passed_audio_files( self, walk_mock ) -> None: + export_dir = Path("/tmp/export") + temp_dir = Path("/tmp/x") + walk_mock.return_value = _create_walk_files( [ "proj stem2.wav", @@ -122,60 +131,68 @@ def test_discover_audiofiles_returns_explicit_passed_audio_files( ] ) - trackpacker = TrackPacker("proj", "/tmp/export") - (master, stems) = trackpacker.discover_audiofiles( - _files_in_dir( - "/tmp/export", ["/tmp/x/proj stem1.wav", "/tmp/x/proj stem3.wav"] - ) - ) - walk_mock.assert_called_with("/tmp/export") + trackpacker = TrackPacker("proj", str(export_dir)) + + explicit_files_list = ["/tmp/x/proj stem1.wav", "/tmp/x/proj stem3.wav"] + + (master, stems) = trackpacker.discover_audiofiles(explicit_files_list) + walk_mock.assert_called_with(str(export_dir)) self.assertEqual("proj.wav", master) self.assertListEqual( - _files_in_dir("/tmp/x/", ["proj stem1.wav", "proj stem3.wav"]), stems + _files_in_dir(temp_dir, ["proj stem1.wav", "proj stem3.wav"]), stems ) @patch("trackpack.trackpacker.ZipFile", autospec=True) def test_pack_files_creates_archive_of_stems(self, zip_mock) -> None: - trackpacker = TrackPacker("projname", "/tmp/proj/Export") + export_dir = Path("/tmp/proj/Export") + trackpacker = TrackPacker("projname", str(export_dir)) + + files_to_pack = _files_in_dir(export_dir, ["a.wav", "b.wav", "c.wav"]) + trackpacker.pack_files( "archivename", - _files_in_dir("/tmp/proj/Export", ["a.wav", "b.wav", "c.wav"]), + files_to_pack, ) zip_mock.assert_has_calls( _create_zip_mock_calls( "archivename", - "/tmp/proj/Export", + str(export_dir), {"a.wav": "a.wav", "b.wav": "b.wav", "c.wav": "c.wav"}, ) ) @patch("trackpack.trackpacker.ZipFile", autospec=True) def test_pack_files_removes_project_name_from_stems(self, zip_mock) -> None: - trackpacker = TrackPacker("proj1", "/tmp/x") - trackpacker.pack_files( - "archive1", _files_in_dir("/tmp/x", ["proj1 a.wav", "b.wav", "proj1 c.wav"]) + export_dir = Path("/tmp/x") + trackpacker = TrackPacker("proj1", str(export_dir)) + + files_to_pack = _files_in_dir( + export_dir, ["proj1 a.wav", "b.wav", "proj1 c.wav"] ) + + trackpacker.pack_files("archive1", files_to_pack) zip_mock.assert_has_calls( _create_zip_mock_calls( "archive1", - "/tmp/x", + str(export_dir), {"proj1 a.wav": "a.wav", "b.wav": "b.wav", "proj1 c.wav": "c.wav"}, ) ) @patch("trackpack.trackpacker.ZipFile", autospec=True) def test_pack_files_replaces_blanks_in_names(self, zip_mock) -> None: - trackpacker = TrackPacker("proj1", "/tmp/st u v w") - trackpacker.pack_files( - "archive1", - _files_in_dir( - "/tmp/st u v w", ["proj1 a a a.wav", "b 123.wav", "proj1 cd efg.wav"] - ), + export_dir = Path("/tmp/st u v w") + trackpacker = TrackPacker("proj1", str(export_dir)) + + files_to_pack = _files_in_dir( + export_dir, ["proj1 a a a.wav", "b 123.wav", "proj1 cd efg.wav"] ) + + trackpacker.pack_files("archive1", files_to_pack) zip_mock.assert_has_calls( _create_zip_mock_calls( "archive1", - "/tmp/st u v w", + str(export_dir), { "proj1 a a a.wav": "a-a-a.wav", "b 123.wav": "b-123.wav", @@ -185,8 +202,8 @@ def test_pack_files_replaces_blanks_in_names(self, zip_mock) -> None: ) -def _files_in_dir(dirpath: str, filenames: list[str]) -> list[str]: - return [os.path.join(dirpath, file) for file in filenames] +def _files_in_dir(dirpath: Path, filenames: list[str]) -> list[Path]: + return [Path(dirpath) / file for file in filenames] def _create_walk_files(files: list[str]): @@ -196,16 +213,16 @@ def _create_walk_files(files: list[str]): def _create_zip_mock_calls( archive_name: str, proj_export_dir: str, files: dict[str, str] ): + proj_export_dir_path = Path(proj_export_dir) + call_list = [ - call(os.path.join(proj_export_dir, f"{archive_name}.zip"), "w"), + call(proj_export_dir_path / f"{archive_name}.zip", "w"), call().__enter__(), # pylint: disable=unnecessary-dunder-call ] for name, entry in files.items(): + file_path = proj_export_dir_path / name # pylint: disable=unnecessary-dunder-call - call_list.append( - call().__enter__().write(os.path.join(proj_export_dir, name), entry) - ) + call_list.append(call().__enter__().write(file_path, entry)) call_list.append(call().__exit__(None, None, None)) - return call_list diff --git a/trackpack/__main__.py b/trackpack/__main__.py index 10dbce2..85006fc 100644 --- a/trackpack/__main__.py +++ b/trackpack/__main__.py @@ -16,6 +16,7 @@ # along with this program. If not, see . import sys +from pathlib import Path from trackpack import cli from trackpack.config import Config @@ -28,9 +29,10 @@ def __fail(msg: str) -> None: def __read_config(filename: str) -> Config: - with open(filename, "r", encoding="utf-8") as config_file: + config_path = Path(filename) + with config_path.open("r", encoding="utf-8") as config_file: cfg = Config() - cfg.load_from_yaml(config_file) + cfg.load_from_yaml(config_file.read()) return cfg @@ -46,7 +48,7 @@ def main() -> None: (_, stems) = trackpacker.discover_audiofiles(args.pack_explicit_files) trackpacker.pack_files(cfg.archive_name, stems) except FileNotFoundError as ex: - __fail(f"{ex.strerror}: {ex.filename}") + __fail(f"Configuration or file not found: '{ex.filename}'") if __name__ == "__main__": diff --git a/trackpack/config.py b/trackpack/config.py index 7b3e565..d038ef0 100644 --- a/trackpack/config.py +++ b/trackpack/config.py @@ -31,7 +31,7 @@ def __init__(self) -> None: def archive_name(self) -> str: if self.append_date: today_date = datetime.now(timezone.utc).date() - return "-".join((self._archive_name, today_date.strftime("%Y-%m-%d"))) + return f"{self._archive_name}-{today_date.strftime('%Y-%m-%d')}" return self._archive_name @archive_name.setter @@ -41,10 +41,14 @@ def archive_name(self, archive_name: str) -> None: else: self._archive_name = archive_name - def load_from_yaml(self, yaml_content) -> None: - config = yaml.safe_load(yaml_content) - self.name = config["name"] - self.archive_name = config.get("archive_name", self.name) + def load_from_yaml(self, yaml_content: str) -> None: + try: + config = yaml.safe_load(yaml_content) + except yaml.YAMLError as e: + raise ValueError(f"Error parsing YAML content: {e}") from e + + self.name = config.get("name", "unnamed") + self._archive_name = config.get("archive_name", self.name) self.append_date = config.get("append_date", False) def load_from_cli_args(self, cli_args) -> None: diff --git a/trackpack/trackpacker.py b/trackpack/trackpacker.py index a7d8c80..2e0b9a6 100644 --- a/trackpack/trackpacker.py +++ b/trackpack/trackpacker.py @@ -16,6 +16,7 @@ # along with this program. If not, see . import os +from pathlib import Path from zipfile import ZipFile @@ -25,37 +26,35 @@ class MissingFileException(Exception): class TrackPacker: def __init__(self, project_name: str, export_dir: str) -> None: - self.__project_name = project_name - self.__export_dir = export_dir + self._project_name = project_name + self._export_dir = Path(export_dir) def discover_audiofiles(self, explicit_files: list[str] | None = None): - (_, _, filenames) = next(os.walk(self.__export_dir)) - master = f"{self.__project_name}.wav" - files = list(filter(lambda f: f.endswith(".wav"), filenames)) + filenames = [f for _, _, files in os.walk(str(self._export_dir)) for f in files] + + master = f"{self._project_name}.wav" + files = [f for f in filenames if f.endswith(".wav")] if master not in files: raise MissingFileException("Master track not found") files.remove(master) if explicit_files: - files = [os.path.abspath(file) for file in explicit_files] + files = [Path(file).resolve() for file in explicit_files] else: - files = [ - os.path.abspath(os.path.join(self.__export_dir, file)) for file in files - ] + files = [self._export_dir / f for f in files] if not files: raise MissingFileException("No stems found") - return (master, [os.path.abspath(file) for file in files]) + return (master, [f.resolve() for f in files]) - def pack_files(self, archive_name: str, files: list[str]): - with ZipFile( - f"{os.path.join(self.__export_dir, archive_name)}.zip", "w" - ) as archive: + def pack_files(self, archive_name: str, files: list[Path]): + archive_path = self._export_dir / f"{archive_name}.zip" + with ZipFile(archive_path, "w") as archive: for file in files: - archive.write(file, self.__normalize_stem_name(os.path.basename(file))) + archive.write(file, self._normalize_stem_name(file.name)) - def __normalize_stem_name(self, stem_name: str) -> str: - stem_name = stem_name.removeprefix(self.__project_name) + def _normalize_stem_name(self, stem_name: str) -> str: + stem_name = stem_name.removeprefix(self._project_name) return stem_name.strip().replace(" ", "-")