Skip to content

Commit

Permalink
Fix errors when writing async packs
Browse files Browse the repository at this point in the history
  • Loading branch information
kmontag committed Jul 1, 2024
1 parent e39bfcd commit 5d4d461
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 24 deletions.
49 changes: 32 additions & 17 deletions src/alpacks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ def __init__(self, **k: Unpack[PackProperties]):
self._is_hidden_in_browse_groups = k.get("is_hidden_in_browse_groups", False)

self.__context: _Context | None = None
# The `open()` method might return None, so we need a separate
# tracker to check the open status.
self.__has_context: bool = False

# Propagate unexpected keys up to `object`, so that errors
# will be thrown if appropriate.
Expand Down Expand Up @@ -114,20 +117,21 @@ async def close(self, context: _Context) -> None:
# Which is equivalent to:
#
# p = PackWriter(**args)
# context = p._open()
# context = p.open()
# try:
# p.set_file(...)
# p.set_preview(...)
# p._commit()
# p.commit()
# finally:
# p._close(context)
# p.close(context)
#
async def __aenter__(self) -> Self:
if self.__context is not None:
if self.__has_context:
msg = f"{self} is already open"
raise ValueError(msg)

self.__context = await self.open()
self.__has_context = True
return self

async def __aexit__(
Expand All @@ -136,15 +140,20 @@ async def __aexit__(
exc_inst: BaseException | None,
exc_tb: TracebackType | None,
) -> None:
if self.__context is None:
if not self.__has_context:
msg = f"{self} is not open"
raise ValueError(msg)

try:
if exc_type is None:
await self.commit()
finally:
await self.close(self.__context)
await self.close(
# The context type is allowed to be `None`, so we
# can't just assert that this is present.
self.__context, # type: ignore
)
self.__has_context = False


# For synchronous writes, just wrap an async writer.
Expand Down Expand Up @@ -177,7 +186,7 @@ def close(self, context: _Context) -> None:
asyncio.run(self._pack_writer_async.close(context))

def __enter__(self) -> Self:
self._context = self.open()
asyncio.run(self._pack_writer_async.__aenter__())
return self

def __exit__(
Expand All @@ -186,11 +195,7 @@ def __exit__(
exc_val: BaseException | None,
exc_tb: TracebackType | None,
) -> None:
try:
if exc_type is None:
self.commit()
finally:
self.close(self._context)
asyncio.run(self._pack_writer_async.__aexit__(exc_type, exc_val, exc_tb))


class DirectoryPackWriterAsync(PackWriterAsync[None]):
Expand All @@ -201,7 +206,9 @@ def __init__(self, output_dir: str, **k: Unpack[PackProperties]):

# If the output dir exists and is non-empty (or is not a
# directory), raise an error.
if os.path.exists(output_dir) and (not os.path.isdir(output_dir) or os.listdir(output_dir)):
if os.path.exists(output_dir) and (
not os.path.isdir(output_dir) or os.listdir(output_dir)
):
msg = f"Output directory '{output_dir}' exists and is not empty."
raise ValueError(msg)

Expand Down Expand Up @@ -262,7 +269,9 @@ async def _write_properties_file(self) -> None:
"""
).lstrip()

await self._write_to_path(os.path.join(FOLDER_INFO_DIR, PROPERTIES_FILE), text.encode("utf-8"))
await self._write_to_path(
os.path.join(FOLDER_INFO_DIR, PROPERTIES_FILE), text.encode("utf-8")
)

# Write pack metadata, e.g. tags.
async def _write_xmp_file(self) -> None:
Expand All @@ -273,7 +282,9 @@ async def _write_xmp_file(self) -> None:
if len(tag_values) == 0:
msg = f"Tag `{tag_name}` is empty for path `{path}`"
raise ValueError(msg)
rdf_items.append("|".join(escape(val) for val in [tag_name, *tag_values]))
rdf_items.append(
"|".join(escape(val) for val in [tag_name, *tag_values])
)

rdf_indent = " "
tags_text += textwrap.indent(
Expand All @@ -285,7 +296,9 @@ async def _write_xmp_file(self) -> None:
<rdf:Bag>
"""
).lstrip("\n")
+ "\n".join([f" <rdf:li>{rdf_item}</rdf:li>" for rdf_item in rdf_items])
+ "\n".join(
[f" <rdf:li>{rdf_item}</rdf:li>" for rdf_item in rdf_items]
)
+ textwrap.dedent(
"""
</rdf:Bag>
Expand Down Expand Up @@ -322,7 +335,9 @@ async def _write_xmp_file(self) -> None:
</x:xmpmeta>
"""
).lstrip()
await self._write_to_path(os.path.join(FOLDER_INFO_DIR, XMP_FILE), xmp_text.encode("utf-8"))
await self._write_to_path(
os.path.join(FOLDER_INFO_DIR, XMP_FILE), xmp_text.encode("utf-8")
)

def _preview_path(self, path: str) -> str:
return os.path.join(FOLDER_INFO_DIR, "Previews", f"{path}.ogg")
Expand Down
39 changes: 32 additions & 7 deletions tests/test_alpacks.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import os
import asyncio
import tempfile

from alpacks import DirectoryPackWriter
import alpacks


def test_simple_directory() -> None:
def test_directory() -> None:
name = "Test"
unique_id = "test.id"
with tempfile.TemporaryDirectory() as output_dir:
path = "test.txt"
version = "2.3.4"
with DirectoryPackWriter(
with alpacks.DirectoryPackWriter(
output_dir,
name=name,
unique_id=unique_id,
Expand All @@ -24,10 +25,14 @@ def test_simple_directory() -> None:

with open(os.path.join(output_dir, path)) as f:
assert f.read() == "test-content"
with open(os.path.join(output_dir, "Ableton Folder Info", "Previews", f"{path}.ogg")) as f:
with open(
os.path.join(output_dir, "Ableton Folder Info", "Previews", f"{path}.ogg")
) as f:
assert f.read() == "test-preview-content"

with open(os.path.join(output_dir, "Ableton Folder Info", "Properties.cfg")) as f:
with open(
os.path.join(output_dir, "Ableton Folder Info", "Properties.cfg")
) as f:
properties_text = f.read()
assert f'String PackUniqueID = "{unique_id}";' in properties_text
assert f'String PackDisplayName = "{name}";' in properties_text
Expand All @@ -39,13 +44,33 @@ def test_simple_directory() -> None:
assert f"Int {field} = {value};" in properties_text

xmp_files = [
file for file in os.listdir(os.path.join(output_dir, "Ableton Folder Info")) if file.endswith(".xmp")
file
for file in os.listdir(os.path.join(output_dir, "Ableton Folder Info"))
if file.endswith(".xmp")
]
assert len(xmp_files) == 1

xmp_file_path = os.path.join(output_dir, "Ableton Folder Info", xmp_files[0])
with open(xmp_file_path) as xmp_file:
xmp_content = xmp_file.read()
assert f"<ablFR:packUniqueId>{unique_id}</ablFR:packUniqueId>" in xmp_content
assert (
f"<ablFR:packUniqueId>{unique_id}</ablFR:packUniqueId>" in xmp_content
)
assert "<ablFR:packVersion>2.3.4</ablFR:packVersion>" in xmp_content
assert "<rdf:li>Tag Name|Tag Value|Subtag Value</rdf:li>" in xmp_content


def test_simple_directory_async() -> None:
async def run() -> None:
with tempfile.TemporaryDirectory() as output_dir:
async with alpacks.DirectoryPackWriterAsync(
output_dir, name="Test", unique_id="test.id"
) as pack_writer:
# Simple test, just make sure the write can happen
# without errors.
await pack_writer.set_file_content("path.adg", b"content")

with open(os.path.join(output_dir, "path.adg")) as f:
assert f.read() == "content"

asyncio.run(run())

0 comments on commit 5d4d461

Please sign in to comment.