From 356f7a5461d28b5bdab7b5b20c949072932d252c Mon Sep 17 00:00:00 2001 From: offa <8887756+offa@users.noreply.github.com> Date: Sat, 10 Oct 2026 19:55:57 +0200 Subject: [PATCH 1/5] Use pathlib instead of string paths --- test/__init__.py | 1 + test/trackpack_test.py | 92 ++++++++++++++++++++++++---------------- trackpack/__init__.py | 1 + trackpack/__main__.py | 8 ++-- trackpack/config.py | 16 ++++--- trackpack/trackpacker.py | 36 ++++++++-------- 6 files changed, 91 insertions(+), 63 deletions(-) diff --git a/test/__init__.py b/test/__init__.py index e69de29..8b13789 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -0,0 +1 @@ + diff --git a/test/trackpack_test.py b/test/trackpack_test.py index 3fded61..44f4a07 100644 --- a/test/trackpack_test.py +++ b/test/trackpack_test.py @@ -18,6 +18,7 @@ import os import unittest from unittest.mock import call, patch +from pathlib import Path from trackpack.trackpacker import MissingFileException, TrackPacker @@ -25,6 +26,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 +38,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 +59,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 +71,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 +91,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 +132,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 +203,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 +214,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/__init__.py b/trackpack/__init__.py index e69de29..8b13789 100644 --- a/trackpack/__init__.py +++ b/trackpack/__init__.py @@ -0,0 +1 @@ + diff --git a/trackpack/__main__.py b/trackpack/__main__.py index 10dbce2..97e3a2f 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_text()) 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..ccc91be 100644 --- a/trackpack/config.py +++ b/trackpack/config.py @@ -16,8 +16,8 @@ # along with this program. If not, see . from datetime import datetime, timezone - import yaml +from pathlib import Path class Config: @@ -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}") + + 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..68517b7 100644 --- a/trackpack/trackpacker.py +++ b/trackpack/trackpacker.py @@ -17,6 +17,7 @@ import os from zipfile import ZipFile +from pathlib import Path class MissingFileException(Exception): @@ -25,37 +26,38 @@ 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 = [] + for root, _, files in os.walk(str(self._export_dir)): + for f in files: + filenames.append(f) + + 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(" ", "-") From 48324073a9637e764bde9fe19676d34e9d78e3b3 Mon Sep 17 00:00:00 2001 From: offa <8887756+offa@users.noreply.github.com> Date: Sat, 10 Oct 2026 22:58:09 +0200 Subject: [PATCH 2/5] Fix imports --- test/trackpack_test.py | 3 +-- trackpack/config.py | 2 +- trackpack/trackpacker.py | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/test/trackpack_test.py b/test/trackpack_test.py index 44f4a07..b907cf4 100644 --- a/test/trackpack_test.py +++ b/test/trackpack_test.py @@ -15,10 +15,9 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import os import unittest -from unittest.mock import call, patch from pathlib import Path +from unittest.mock import call, patch from trackpack.trackpacker import MissingFileException, TrackPacker diff --git a/trackpack/config.py b/trackpack/config.py index ccc91be..bcc7e6e 100644 --- a/trackpack/config.py +++ b/trackpack/config.py @@ -16,8 +16,8 @@ # along with this program. If not, see . from datetime import datetime, timezone + import yaml -from pathlib import Path class Config: diff --git a/trackpack/trackpacker.py b/trackpack/trackpacker.py index 68517b7..33a0491 100644 --- a/trackpack/trackpacker.py +++ b/trackpack/trackpacker.py @@ -16,8 +16,8 @@ # along with this program. If not, see . import os -from zipfile import ZipFile from pathlib import Path +from zipfile import ZipFile class MissingFileException(Exception): From 5c3fd5c190a16b40a93dcf2a9990a0f2726ea4eb Mon Sep 17 00:00:00 2001 From: offa <8887756+offa@users.noreply.github.com> Date: Sat, 10 Oct 2026 23:11:02 +0200 Subject: [PATCH 3/5] Fix linting issues --- test/__init__.py | 1 - trackpack/__init__.py | 1 - trackpack/config.py | 2 +- trackpack/trackpacker.py | 2 +- 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/test/__init__.py b/test/__init__.py index 8b13789..e69de29 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -1 +0,0 @@ - diff --git a/trackpack/__init__.py b/trackpack/__init__.py index 8b13789..e69de29 100644 --- a/trackpack/__init__.py +++ b/trackpack/__init__.py @@ -1 +0,0 @@ - diff --git a/trackpack/config.py b/trackpack/config.py index bcc7e6e..d038ef0 100644 --- a/trackpack/config.py +++ b/trackpack/config.py @@ -45,7 +45,7 @@ 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}") + 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) diff --git a/trackpack/trackpacker.py b/trackpack/trackpacker.py index 33a0491..dd960eb 100644 --- a/trackpack/trackpacker.py +++ b/trackpack/trackpacker.py @@ -31,7 +31,7 @@ def __init__(self, project_name: str, export_dir: str) -> None: def discover_audiofiles(self, explicit_files: list[str] | None = None): filenames = [] - for root, _, files in os.walk(str(self._export_dir)): + for _, _, files in os.walk(str(self._export_dir)): for f in files: filenames.append(f) From 61c878c0cd098c990c1696db29d833545b3dc7f3 Mon Sep 17 00:00:00 2001 From: offa <8887756+offa@users.noreply.github.com> Date: Sat, 10 Oct 2026 23:18:36 +0200 Subject: [PATCH 4/5] Fix linting issue --- trackpack/trackpacker.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/trackpack/trackpacker.py b/trackpack/trackpacker.py index dd960eb..2e0b9a6 100644 --- a/trackpack/trackpacker.py +++ b/trackpack/trackpacker.py @@ -30,10 +30,7 @@ def __init__(self, project_name: str, export_dir: str) -> None: self._export_dir = Path(export_dir) def discover_audiofiles(self, explicit_files: list[str] | None = None): - filenames = [] - for _, _, files in os.walk(str(self._export_dir)): - for f in files: - filenames.append(f) + 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")] From 638193815fdaf52b3d0209bd44d3cbf6f564e5d3 Mon Sep 17 00:00:00 2001 From: offa <8887756+offa@users.noreply.github.com> Date: Sat, 10 Oct 2026 23:23:33 +0200 Subject: [PATCH 5/5] Fix linting issue --- trackpack/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trackpack/__main__.py b/trackpack/__main__.py index 97e3a2f..85006fc 100644 --- a/trackpack/__main__.py +++ b/trackpack/__main__.py @@ -32,7 +32,7 @@ def __read_config(filename: str) -> Config: config_path = Path(filename) with config_path.open("r", encoding="utf-8") as config_file: cfg = Config() - cfg.load_from_yaml(config_file.read_text()) + cfg.load_from_yaml(config_file.read()) return cfg