RFC 0003 — Blob als Grundlage aller Datenformate (+ Blob-Härtung)¶
| Feld | Wert |
|---|---|
| Status | Accepted — Teil A vollständig implementiert; Teil B offen |
| Datum | 2026-06-29 |
| Autor | lepy lepy@tuta.io |
| Komponente | sdata/sclass/blob.py (+ dataframe.py, image.py, filereference.py) |
| Betrifft | Blob als gemeinsame Content-/Integritäts-/Provenienz-Basis |
| Validierung | Teil A komplett; tests/test_sclass_blob_{hardening,io}.py, blob.py 100 % |
Umsetzungsstand. Teil A vollständig umgesetzt (rückwärtskompatibel, kanonische CI grün,
blob.py100 %): B1sha256-Property; B2 Auto-Befüllungmime_type(ausfiletype) undcreation_date(aus dem reservierten_sdata_ctime— kein zweiter Zeitstempel); B3 Cache als nicht-serialisiertes Instanzattribut_content_cache(nicht mehr inself.data); B4saf:→Standard-Vokabular (schema:sha256,dcat:mediaType,dcterms:source/created/modified/publisher/license); B5write(uri)+open(mode)(fsspec;openstreamt URIs,io.BytesIOfür bytes); B6verify()+update_checksum(); B7size-Property. Aus Teil B (Foundation, Option 3 gestaffelt) umgesetzt:FileReference(Blob)— die Datei alsuri-Content (Pfad bleibt erhalten, ging zuvor verloren); erbtcontent_bytes/open/exists/verify/size(blob.py/filereference.py100 %).Image(Blob)— stabilisiert und neu aufBlobaufgesetzt: Bild als Content, Pillow nur lazy (pil/to_numpy/save), PNG-Metadaten-Embedding; die kaputten Alt-Pfade (saveas/_from_filepath/piexif/PIL.Image-Importbug) entfernt;image.pybleibt inomit(Pillow nicht als CI-Dep deklariert), verifiziert viatests/test_image.py(importorskip("PIL")). Offen:DataFrame(Blob)als eigener Folge-RFC.
1. Zusammenfassung¶
Blob soll die gemeinsame Grundlage aller Datenformat-Container werden: der
Layer, der Inhalt (Bytes oder URI), Integrität (Prüfsumme/verify),
Provenienz/Lizenz und lazy/streamendes Laden (fsspec) bereitstellt —
während formatspezifische Klassen (DataFrame, Image, FileReference) nur die
Kodierung des Inhalts liefern (Parquet, PNG, …).
Heute ist das nicht realisiert: kein Container erbt von Blob (obwohl
filereference.py/zipfile.py/dataframe.py auskommentierte Blob-Importe
tragen — die ursprüngliche Absicht). Zugleich hat die aktuelle Blob-Klasse
mehrere konkrete Schwächen. Dieses RFC schlägt (A) eine rückwärtskompatible
Härtung von Blob und (B) den gestaffelten Umbau zur echten Foundation vor.
2. Kontext¶
Blob(Base) speichert den Inhalt in self.data['content']:
- Laden erfolgt lazy über
content_bytes(für URIs viafsspec; Extrasdata[blob]), Ergebnis wird inself.data['content_cached']zwischengespeichert. Blobrechnetsha1/md5;DEFAULT_METADATAdeklariert u. a.checksum,mime_type,creation_date,license,source_uri.Blobist mit 100 % Coverage getestet — es ist funktionsfähig; dieses RFC adressiert Design-/Konsistenz-Schwächen, keine Abstürze.
Klassenlage: DataFrame(Base), Image(Base), FileReference(Base) — keiner
erbt von Blob.
3. Befunde (verifiziert)¶
B1 — Prüfsummen-Inkonsistenz. DEFAULT_METADATA["checksum"] deklariert
SHA-256 (required: True), die Klasse berechnet aber nur sha1/md5 und
befüllt checksum nie. Es gibt kein sha256.
B2 — required: True-Defaults werden nie befüllt. checksum, mime_type,
creation_date sind required, werden aber nicht automatisch gesetzt →
blob.validate() (gegen ein Schema) bzw. eine required-Prüfung schlägt
strukturell fehl.
B3 — Cache liegt im serialisierbaren data. content_cached wird in
self.data abgelegt; to_dict/exists müssen ihn defensiv popen. Der Cache
gehört nicht in den serialisierten Zustand (Mutation, Gleichheit, versehentliche
Persistenz großer Bytes).
B4 — Undefinierter Ontologie-Prefix saf:. DEFAULT_METADATA nutzt 8×
saf:checksum/saf:mimeType/… — saf: ist nicht im JSON-LD-@context
(sdata/vocab.py) registriert → in JSON-LD entstehen unauflösbare IRIs.
B5 — Fehlende Persistenz/Streaming-API. Kein write(uri)/save_as, kein
open()-Handle, kein chunked Read. content_bytes lädt alles in den RAM —
widerspricht dem „Large Object"-Anspruch.
B6 — Keine Integritätsprüfung. Es wird (sollte) eine Prüfsumme gespeichert,
aber es gibt kein verify(), das den Inhalt dagegen prüft.
B7 — MIME/filetype unverwaltet. filetype (z. B. "pdf") und das
mime_type-Metadatum stehen unverbunden nebeneinander; keine Ableitung
filetype↔mime_type, keine size.
F1 — Foundation nicht realisiert. Content-/Hash-/Integritäts-/Provenienz-Logik
ist nicht geteilt. DataFrame bettet seine Parquet-Bytes selbst in data ein
statt über einen gemeinsamen Blob-Content-Layer — Duplikation und divergierende
Serialisierung.
4. Ziele / Nicht-Ziele¶
Ziele
Blobrückwärtskompatibel härten (B1–B7).Blobzur gemeinsamen Content-/Integritäts-/Provenienz-Basis machen, die formatspezifische Klassen wiederverwenden.
Nicht-Ziele
- Keine Brüche am
to_dict/from_dict-Layout bestehender Container. - Kein Zwang,
DataFramesofort aufBlobumzustellen (eigener Folge-RFC). - Keine neue Pflicht-Abhängigkeit (fsspec bleibt Extra
sdata[blob]).
5. Teil A — Blob härten (rückwärtskompatibel)¶
- Prüfsummen vereinheitlichen (B1/B6).
sha256-Property ergänzen;checksumbeim ersten Laden lazy mit dem SHA-256 befüllen (zur deklarierten Metadatenform passend).verify() -> boolergänzt: Inhalt gegen gespeichertechecksumprüfen.sha1/md5bleiben (Kompat). - Cache aus
datalösen (B3). Geladene Bytes in einem nicht serialisierten Instanzattributself._content_cachehalten (nicht inself.data);to_dictmuss dann nichts mehrpopen. saf:auflösen (B4). Entweder Prefixsaf:insdata/vocab.py+@contextregistrieren (mit dereferenzierbarer IRI unterlepy.github.io/sdata/ns#) oder auf Standard-Vokabular abbilden (spdx:checksum/schema:sha256,schema:encodingFormatfür MIME,dcterms:created/modified,dcterms:license,dcat:downloadURL/dcterms:source). Empfehlung: Standard-Vokabular (Interop), Prefix-Registrierung als Fallback.required-Defaults sinnvoll befüllen (B2). Beim Setzen von Inhalt:mime_typeausfiletypeableiten (Stdlibmimetypes),creation_datestempeln;checksumlazy. Felder, die nicht zuverlässig auto-befüllbar sind, nichtrequiredlassen.- Persistenz/Streaming (B5/B7).
write(uri, **kwargs) -> str(Inhalt an ein fsspec-Ziel schreiben),open(mode="rb")(streamendes Handle),size-Property; chunked Hashing direkt aus dem Stream ohne Voll-Load.
Alles additiv: bestehende Signaturen/content-Layout bleiben; neue Methoden/
Properties kommen hinzu.
6. Teil B — Blob als Foundation¶
Klarstellung der „Grundlage": Blob liefert den Content-/Integritäts-/
Provenienz-Layer; Format-Klassen liefern die Kodierung.
Option 1 — Volle Vererbung (DataFrame(Blob), Image(Blob), …)¶
Jede Formatklasse serialisiert ihr Payload in den Blob-Content (bytes/uri).
Pro: maximale Wiederverwendung, einheitliche Identität/Integrität.
Contra: invasiv; DataFrame hat ein bewusst mehrformatiges Serialisierungs-
modell (Parquet/Arrow/CSV/HDF5/…) und ein etabliertes to_dict-Layout → Bruchrisiko.
Option 2 — Content-Mixin / ContentStore-Helper¶
Blobs Content-/Hash-/Integritätslogik als wiederverwendbares Mixin bzw.
Hilfsklasse, die Container optional einbinden, ohne die Basisklasse zu wechseln.
Pro: additiv, geringes Risiko. Contra: keine echte „is-a Blob"-Hierarchie.
Option 3 — Gestaffelt (empfohlen)¶
- Teil A umsetzen (rein additiv, eigener PR).
FileReferenceundImageaufBlobheben — die auskommentierten Importe realisieren (kleine, isolierte PRs); beide sind „content-zentrisch" und passen natürlich aufBlob.DataFrame(Blob)nur prüfen, wenn ohne Bruch desto_dict-Layouts möglich; sonst Content-Mixin (Option 2) für die gemeinsamen Teile. → eigener Folge-RFC.
7. Kompatibilität / Migration¶
- Teil A strikt additiv;
content-Layout undto_dict/from_dictunverändert. - Foundation gestaffelt, jeweils mit Round-Trip-Tests;
image.pyist heute aus der Coverageomit(experimentell) — vor einem Umbau zu klären. - Optional-Dependency-Muster (
try import fsspec … except ImportError) bleibt.
8. Testplan / Coverage¶
- A1 Prüfsummen:
sha256korrekt;checksumwird lazy befüllt;verify()True/False (intakt vs. manipuliert). - A2 Cache: nach
content_bytesist_content_cachegesetzt,to_dict()enthält keincontent_cached; Round-Trip bleibt gleich. - A3
saf:/Vokabular:to_jsonld()erzeugt nur auflösbare Terme (gegen das@contextgeprüft). - A4 required-Defaults:
mime_type/creation_datenach Set gesetzt;validate()gegen ein Blob-Schema grün. - A5 Persistenz/Streaming:
write(uri)→exists();open()liest identische Bytes; Hash aus Stream == Hash auscontent_bytes. - Optional-Pfade via
importorskip/injiziertes Fake (nurtry/except ImportError# pragma: no cover), damit die kanonische CI 100 % bleibt.
9. Risiken / offene Punkte¶
DataFrame(Blob)-Kompatibilität — das mehrformatige Serialisierungsmodell und dasto_dict-Layout dürfen nicht brechen (Folge-RFC, Option 2 als Fallback).saf:-Entscheidung — Standard-Vokabular vs. eigener registrierter Prefix; mit dem bestehenden JSON-LD-/Namespace-Programm (RFC-fremd) abstimmen.required-Semantik vs.Base.SDATA_SCHEMA/validate()— konsistent halten.image.pyist experimentell/omit — Umbau erst nach Stabilisierung.- fsspec-Fehlerbilder (
content_byteswrappt inValueError,exists()separat) bei der Streaming-API vereinheitlichen.