Files
strukto-ai--mirage/python/mirage/workspace/snapshot/tar_io.py
T
wehub-resource-sync bcbd1bdb22
Integ / changes (push) Has been skipped
Pre-commit / pre-commit (push) Failing after 1s
CLI exit codes / changes (push) Has been skipped
Test (Install) / changes (push) Has been skipped
Test (Python) / changes (push) Has been skipped
Test (TypeScript) / changes (push) Has been skipped
CLI exit codes / cli-gate (push) Has been cancelled
Test (Install) / test-install-gate (push) Has been cancelled
Integ / integ-gate (push) Has been cancelled
Test (Python) / test-python-gate (push) Has been cancelled
Test (TypeScript) / test-typescript-gate (push) Has been cancelled
Test (Install) / python-minimal (3.12) (push) Has been cancelled
Test (Install) / python-minimal (3.11) (push) Has been cancelled
Test (Install) / python-extra (agno, mirage.agents.agno) (push) Has been cancelled
Test (Install) / python-extra (chroma, mirage.resource.chroma) (push) Has been cancelled
Test (Install) / python-extra (pdf, mirage.core.filetype.pdf) (push) Has been cancelled
Integ / integ (push) Has been cancelled
Integ / integ-database (push) Has been cancelled
Integ / integ-database-ts (push) Has been cancelled
Integ / integ-data (push) Has been cancelled
Integ / integ-ssh (push) Has been cancelled
Integ / integ-ssh-ts (push) Has been cancelled
Test (Python) / audit (push) Has been cancelled
Test (TypeScript) / test (push) Has been cancelled
Test (TypeScript) / python-fs-shim (push) Has been cancelled
CLI exit codes / Python CLI (push) Has been cancelled
CLI exit codes / TypeScript CLI (push) Has been cancelled
CLI exit codes / Cross-language snapshot interop (push) Has been cancelled
Test (Python) / test (push) Has been cancelled
Test (Python) / import-isolation (deepagents, openai, mirage.agents.openai_agents) (push) Has been cancelled
Test (Python) / import-isolation (deepagents, pydantic-ai, mirage.agents.pydantic_ai) (push) Has been cancelled
Integ / integ-ts (push) Has been cancelled
Integ / integ-fuse (push) Has been cancelled
Test (Install) / python-extra (databricks, mirage.resource.databricks_volume) (push) Has been cancelled
Test (Install) / python-extra (deepagents, mirage.agents.langchain) (push) Has been cancelled
Test (Install) / python-extra (email, mirage.resource.email) (push) Has been cancelled
Test (Install) / python-extra (fuse, mirage.fuse.mount) (push) Has been cancelled
Test (Install) / python-extra (hdf5, mirage.core.filetype.hdf5) (push) Has been cancelled
Test (Install) / python-extra (hf, mirage.resource.hf_buckets) (push) Has been cancelled
Test (Install) / python-extra (lancedb, mirage.resource.lancedb) (push) Has been cancelled
Test (Install) / python-extra (langfuse, mirage.resource.langfuse) (push) Has been cancelled
Test (Install) / python-extra (mongodb, mirage.resource.mongodb) (push) Has been cancelled
Test (Install) / python-extra (nextcloud, mirage.resource.nextcloud) (push) Has been cancelled
Test (Install) / python-extra (openai, mirage.agents.openai_agents) (push) Has been cancelled
Test (Install) / python-extra (openhands, mirage.agents.openhands, 3.12) (push) Has been cancelled
Test (Install) / python-extra (parquet, mirage.core.filetype.parquet) (push) Has been cancelled
Test (Install) / python-extra (postgres, mirage.resource.postgres) (push) Has been cancelled
Test (Install) / python-extra (pydantic-ai, mirage.agents.pydantic_ai) (push) Has been cancelled
Test (Install) / python-extra (qdrant, mirage.resource.qdrant) (push) Has been cancelled
Test (Install) / python-extra (redis, mirage.resource.redis) (push) Has been cancelled
Test (Install) / python-extra (s3, mirage.resource.s3) (push) Has been cancelled
Test (Install) / python-extra (ssh, mirage.resource.ssh) (push) Has been cancelled
Test (Install) / ts-minimal (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:44 +08:00

114 lines
3.9 KiB
Python

# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
import io
import json
import tarfile
from mirage.workspace.snapshot.manifest import resolve_manifest
from mirage.workspace.snapshot.utils import is_safe_blob_path
_MANIFEST_NAME = "manifest.json"
_COMPRESS_MODES = {None: "w", "gz": "w:gz", "bz2": "w:bz2", "xz": "w:xz"}
def write_tar(target,
manifest: dict,
blobs: dict[str, bytes],
*,
compress: str | None = None) -> None:
"""Write manifest + blobs as a tar.
Args:
target: filesystem path (str/Path) OR a writable file-like
object with a `write` method (BytesIO, etc.).
manifest: JSON-serializable dict.
blobs: {tar_path: bytes} side-files.
compress: None | "gz" | "bz2" | "xz".
"""
if compress not in _COMPRESS_MODES:
raise ValueError(
f"Unknown compress mode: {compress!r}. "
f"Use one of: {sorted(k for k in _COMPRESS_MODES if k)}")
mode = _COMPRESS_MODES[compress]
if hasattr(target, "write"):
tar = tarfile.open(fileobj=target, mode=mode)
else:
tar = tarfile.open(str(target), mode)
with tar:
manifest_bytes = json.dumps(manifest, indent=2,
default=_json_default).encode("utf-8")
_add(tar, _MANIFEST_NAME, manifest_bytes)
for tar_path, data in blobs.items():
_add(tar, tar_path, data)
def read_tar(source) -> dict:
"""Read a tar produced by write_tar; return a resolved state dict.
Args:
source: filesystem path (str/Path) OR a readable file-like
object with a `read` method.
"""
if hasattr(source, "read"):
tar = tarfile.open(fileobj=source, mode="r:*")
else:
tar = tarfile.open(str(source), "r:*")
with tar:
member = tar.getmember(_MANIFEST_NAME)
f = tar.extractfile(member)
if f is None:
raise ValueError(f"{_MANIFEST_NAME} missing or unreadable")
manifest = json.loads(f.read().decode("utf-8"))
return resolve_manifest(manifest, _make_reader(tar))
def _make_reader(tar):
def reader(blob_path: str) -> bytes:
if not is_safe_blob_path(blob_path):
raise ValueError(f"Unsafe blob path: {blob_path!r}")
try:
member = tar.getmember(blob_path)
except KeyError as exc:
raise ValueError(
f"Manifest references missing blob: {blob_path!r}") from exc
f = tar.extractfile(member)
if f is None:
raise ValueError(f"Blob unreadable: {blob_path!r}")
return f.read()
return reader
def _add(tar: tarfile.TarFile, name: str, data: bytes) -> None:
info = tarfile.TarInfo(name=name)
info.size = len(data)
info.mode = 0o644
tar.addfile(info, io.BytesIO(data))
def _json_default(obj):
# StrEnum members serialize as their string value (already happens
# since StrEnum inherits from str), but bytes leftover in the
# manifest are a programmer error — they should have been split
# to blob refs earlier.
if isinstance(obj, bytes):
raise TypeError(
"Bytes leftover in manifest — split_manifest_and_blobs "
"must replace every bytes value with a blob ref")
raise TypeError(
f"Object of type {type(obj).__name__} not JSON serializable")