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