Compare commits

...

4 Commits

Author SHA1 Message Date
evilchili
7ddc9531bb split filepointer in to text and binary variants 2025-10-18 16:54:58 -07:00
evilchili
972d42531c conditional encoding 2025-10-18 16:17:43 -07:00
evilchili
80a4fbd1f6 fix deserialize 2025-10-18 16:14:29 -07:00
evilchili
ab6c7ca551 encode relpath as a string 2025-10-18 16:09:04 -07:00
3 changed files with 66 additions and 9 deletions

View File

@ -1,15 +1,16 @@
from grung.types import ( from grung.types import (
BackReference, BackReference,
BinaryFilePointer,
Collection, Collection,
DateTime, DateTime,
Dict, Dict,
Field, Field,
FilePointer,
Integer, Integer,
List, List,
Password, Password,
Record, Record,
RecordDict, RecordDict,
TextFilePointer,
Timestamp, Timestamp,
) )
@ -50,7 +51,8 @@ class Album(Record):
Dict("credits"), Dict("credits"),
List("tracks"), List("tracks"),
BackReference("artist", Artist), BackReference("artist", Artist),
FilePointer("cover", extension=".jpg"), BinaryFilePointer("cover", extension=".jpg"),
TextFilePointer("review"),
] ]

View File

@ -283,14 +283,14 @@ class BackReference(Pointer):
@dataclass @dataclass
class FilePointer(Field): class BinaryFilePointer(Field):
""" """
Write the contents of this field to disk and store the path in the db. Write the contents of this field to disk and store the path in the db.
""" """
name: str name: str
value_type: type = bytes value_type: type = bytes
extension: str = ".txt" extension: str = ".blob"
def relpath(self, record): def relpath(self, record):
return Path(record._metadata.table) / record[record._metadata.primary_key] / f"{self.name}{self.extension}" return Path(record._metadata.table) / record[record._metadata.primary_key] / f"{self.name}{self.extension}"
@ -298,14 +298,45 @@ class FilePointer(Field):
def deserialize(self, value: str, db: TinyDB, recurse: bool = True) -> value_type: def deserialize(self, value: str, db: TinyDB, recurse: bool = True) -> value_type:
if not value: if not value:
return None return None
return (db.path / value).read_bytes() _, relpath = value.split("::")
return self.value_type((db.path / relpath).read_bytes())
def prepare(self, data: value_type):
if not data:
return
if not isinstance(data, self.value_type):
return data.encode()
return data
def before_insert(self, value: value_type, db: TinyDB, record: Record) -> None: def before_insert(self, value: value_type, db: TinyDB, record: Record) -> None:
if not value:
return
relpath = self.relpath(record) relpath = self.relpath(record)
path = db.path / relpath path = db.path / relpath
path.parent.mkdir(parents=True, exist_ok=True) path.parent.mkdir(parents=True, exist_ok=True)
path.write_bytes(record[self.name]) path.write_bytes(self.prepare(value))
record[self.name] = relpath record[self.name] = f"File::{self.relpath(record)}"
@dataclass
class TextFilePointer(BinaryFilePointer):
"""
Write the contents of this field to disk and store the path in the db.
"""
name: str
value_type: type = str
extension: str = ".txt"
def prepare(self, data: value_type):
if data:
return str(data).encode()
def deserialize(self, value: str, db: TinyDB, recurse: bool = True) -> value_type:
if not value:
return None
_, relpath = value.split("::")
return (db.path / relpath).read_text()
@dataclass @dataclass

View File

@ -156,12 +156,10 @@ def test_mapping(db):
name="The Impossible Kid", name="The Impossible Kid",
credits={"Produced By": "Aesop Rock", "Lyrics By": "Aesop Rock", "Puke in the MeowMix By": "Kirby"}, credits={"Produced By": "Aesop Rock", "Lyrics By": "Aesop Rock", "Puke in the MeowMix By": "Kirby"},
tracks=["Mystery Fish", "Rings", "Lotta Years", "Dorks"], tracks=["Mystery Fish", "Rings", "Lotta Years", "Dorks"],
cover=b"some jpg data",
) )
) )
assert album.credits["Produced By"] == "Aesop Rock" assert album.credits["Produced By"] == "Aesop Rock"
assert album.tracks[0] == "Mystery Fish" assert album.tracks[0] == "Mystery Fish"
assert album.cover == b"some jpg data"
aes = db.save( aes = db.save(
examples.Artist( examples.Artist(
@ -176,5 +174,31 @@ def test_mapping(db):
assert aes.albums[album.name].uid == album.uid assert aes.albums[album.name].uid == album.uid
assert "Kirby" in aes.albums[album.name].credits.values() assert "Kirby" in aes.albums[album.name].credits.values()
def test_file_pointers(db):
album = db.save(
examples.Album(
name="The Impossible Kid",
credits={"Produced By": "Aesop Rock", "Lyrics By": "Aesop Rock", "Puke in the MeowMix By": "Kirby"},
tracks=["Mystery Fish", "Rings", "Lotta Years", "Dorks"],
cover=b"some jpg data",
review="10/10 no notes",
)
)
assert album.cover == b"some jpg data"
assert album.review == "10/10 no notes"
db.save(
examples.Artist(
name="Aesop Rock",
albums={"The Impossible Kid": album},
)
)
album = db.Album.get(doc_id=album.doc_id)
location_on_disk = db.path / album._metadata.fields["cover"].relpath(album) location_on_disk = db.path / album._metadata.fields["cover"].relpath(album)
assert location_on_disk.read_bytes() == album.cover assert location_on_disk.read_bytes() == album.cover
location_on_disk = db.path / album._metadata.fields["review"].relpath(album)
assert location_on_disk.read_text() == album.review