split filepointer in to text and binary variants

This commit is contained in:
evilchili 2025-10-18 16:54:58 -07:00
parent 972d42531c
commit 7ddc9531bb
3 changed files with 66 additions and 11 deletions

View File

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

View File

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

View File

@ -156,12 +156,10 @@ def test_mapping(db):
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",
)
)
assert album.credits["Produced By"] == "Aesop Rock"
assert album.tracks[0] == "Mystery Fish"
assert album.cover == b"some jpg data"
aes = db.save(
examples.Artist(
@ -176,5 +174,31 @@ def test_mapping(db):
assert aes.albums[album.name].uid == album.uid
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)
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