Blob is no more. Notes now always have content.

This commit is contained in:
Andrew Mulbrook 2026-03-26 00:36:01 -05:00
parent 5df3c81417
commit ffefe4dd21
4 changed files with 28 additions and 48 deletions

View file

@ -4,7 +4,9 @@ import tempfile
import datetime as dt import datetime as dt
from pathlib import Path from pathlib import Path
from typing import Optional, Dict, List, Self, Iterable from typing import Optional, Dict, List, Self, Iterable
from .trove import Note, Trove, TreeNote, BlobNote, Blob, Tree, BadNoteType, TreeEntry, NoteNotFound
from .trove import Note, Trove, TreeNote, BadNoteType, TreeEntry, NoteNotFound
from . import fs_util as fsu
class FSNote(Note): class FSNote(Note):
@ -61,22 +63,17 @@ class FSNote(Note):
def set_raw_metadata(self, key: str, value: bytes) -> None: def set_raw_metadata(self, key: str, value: bytes) -> None:
self._trove._set_metadata(self._inode, key, value) self._trove._set_metadata(self._inode, key, value)
class FSBlobNote(FSNote, BlobNote): def read_content(self) -> bytes:
def read(self) -> bytes: """Read the raw content of the note."""
if self._inode is None: content_file = fsu.get_content_path(self._path)
if content_file.exists():
return content_file.read_bytes()
return b"" return b""
return self._path.read_bytes()
def write(self, data: bytes) -> None: def write_content(self, data:bytes) -> None:
self._path.write_bytes(data) """Write the raw content of the note."""
# Update cache just in case inode changed (some editors do this) content_file = fsu.get_content_path(self._path)
try: content_file.write_bytes(data)
new_inode = self._path.stat().st_ino
if new_inode != self._inode:
self._trove._update_cache(new_inode, self._path)
self._inode = new_inode
except OSError:
pass
class FSTreeNote(FSNote, TreeNote): class FSTreeNote(FSNote, TreeNote):
@property @property
@ -85,7 +82,7 @@ class FSTreeNote(FSNote, TreeNote):
return "inode/directory" return "inode/directory"
def link(self, name: str, note: Note): def link(self, name: str, note: Note):
if not isinstance(note, FSBlobNote): if not isinstance(note, FSNote):
raise BadNoteType("Only blob notes can be linked") raise BadNoteType("Only blob notes can be linked")
target_path = self._path / name target_path = self._path / name
@ -239,7 +236,7 @@ class FSTrove(Trove):
if target_path.is_dir(): if target_path.is_dir():
return FSTreeNote(self, inode=note_id, path=target_path) return FSTreeNote(self, inode=note_id, path=target_path)
else: else:
return FSBlobNote(self, inode=note_id, path=target_path) return FSNote(self, inode=note_id, path=target_path)
def get_raw_note(self, note_id: int) -> Note: def get_raw_note(self, note_id: int) -> Note:
p = self.get_path_by_inode(note_id) p = self.get_path_by_inode(note_id)
@ -248,7 +245,7 @@ class FSTrove(Trove):
return self.get_raw_note_by_path(p) return self.get_raw_note_by_path(p)
def create_blob(self, data: bytes | None = None) -> BlobNote: def create_blob(self, data: bytes | None = None) -> Note:
fd, temp_path = tempfile.mkstemp(dir=self.working) fd, temp_path = tempfile.mkstemp(dir=self.working)
try: try:
if data: if data:
@ -258,7 +255,7 @@ class FSTrove(Trove):
p = Path(temp_path) p = Path(temp_path)
inode = p.stat().st_ino inode = p.stat().st_ino
self._update_cache(inode, p) self._update_cache(inode, p)
return FSBlobNote(self, inode=inode, path=p) return FSNote(self, inode=inode, path=p)
def get_root(self) -> TreeNote: def get_root(self) -> TreeNote:
return FSTreeNote(self, inode=self._root_inode, path=self.root) return FSTreeNote(self, inode=self._root_inode, path=self.root)

View file

@ -18,11 +18,5 @@ class ToolBasicEditor(Tool):
layout.addWidget(self._text_edit) layout.addWidget(self._text_edit)
self.refresh() self.refresh()
def _refresh_blob(self, note: tr.Blob):
self._text_edit.setPlainText(note.read().decode("utf-8"))
def refresh(self): def refresh(self):
if isinstance(self.note, tr.Blob): self._text_edit.setPlainText(self.note.read_content().decode("utf-8"))
self._refresh_blob(cast(tr.Blob, self.note))

View file

@ -56,14 +56,12 @@ class Note(Protocol):
"""Set metadata value for the given key.""" """Set metadata value for the given key."""
... ...
@runtime_checkable def read_content(self) -> bytes:
class Blob(Protocol):
def read(self) -> bytes:
"""Read the raw content of the note.""" """Read the raw content of the note."""
... ...
def write(self, data: bytes) -> None: def write_content(self, data:bytes) -> None:
"""Write new content to the note.""" """Write the raw content of the note."""
... ...
@ -101,10 +99,6 @@ class Tree(Protocol):
"""Return all entries as {name: object_id}.""" """Return all entries as {name: object_id}."""
... ...
@runtime_checkable
class BlobNote(Note, Blob, Protocol):
"""Blob Note"""
@runtime_checkable @runtime_checkable
class TreeNote(Note, Tree, Protocol): class TreeNote(Note, Tree, Protocol):
"""Tree Note""" """Tree Note"""
@ -121,7 +115,7 @@ class Trove(Protocol):
"""Retrieve a note by a object id""" """Retrieve a note by a object id"""
... ...
def create_blob(self, data: bytes | None = None) -> BlobNote: def create_blob(self, data: bytes | None = None) -> Note:
"""Create a new blob node at the given path with content""" """Create a new blob node at the given path with content"""
... ...

View file

@ -13,7 +13,7 @@ from .db import Sqlite3Trove, NOTE_ROOT_ID
from . import trove as tr from . import trove as tr
from .trove import Note, Trove, TreeNote, BlobNote, TreeEntry, NoteNotFound, ObjectId from .trove import Note, Trove, TreeNote, TreeEntry, NoteNotFound, ObjectId
class NoteImpl(Note): class NoteImpl(Note):
@ -50,16 +50,11 @@ class NoteImpl(Note):
def set_raw_metadata(self, key: str, value: bytes) -> None: def set_raw_metadata(self, key: str, value: bytes) -> None:
self._db.write_metadata(self._object_id, key, value) self._db.write_metadata(self._object_id, key, value)
def read_content(self) -> bytes:
class BlobNoteImpl(NoteImpl, BlobNote):
"""Concrete BlobNote: a blob object in the store with metadata access."""
# Blob protocol
def read(self) -> bytes:
data = self._db.read_object(self._object_id) data = self._db.read_object(self._object_id)
return data if data is not None else b"" return data if data is not None else b""
def write(self, data: bytes) -> None: def write_content(self, data: bytes) -> None:
self._db.write_content(self._object_id, data) self._db.write_content(self._object_id, data)
@ -147,13 +142,13 @@ class TroveImpl:
raise NoteNotFound(note_id) raise NoteNotFound(note_id)
if self._db.is_tree(note_id) or info.type == "inode/directory": if self._db.is_tree(note_id) or info.type == "inode/directory":
return TreeNoteImpl(self, note_id) return TreeNoteImpl(self, note_id)
return BlobNoteImpl(self, note_id) return NoteImpl(self, note_id)
def create_blob(self, data: bytes | None = None, def create_blob(self, data: bytes | None = None,
dtype: str = "application/octet-stream") -> BlobNote: dtype: str = "application/octet-stream") -> Note:
"""Create a new blob object and return a BlobNote for it.""" """Create a new blob object and return a BlobNote for it."""
obj_id = self._db.write_blob(data or b"", dtype=dtype) obj_id = self._db.write_blob(data or b"", dtype=dtype)
return BlobNoteImpl(self, obj_id) return NoteImpl(self, obj_id)
def get_root(self) -> TreeNote: def get_root(self) -> TreeNote:
"""Return the root TreeNote (always id=NOTE_ROOT_ID).""" """Return the root TreeNote (always id=NOTE_ROOT_ID)."""