chore: import upstream snapshot with attribution
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
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
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
# ========= 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 asyncio
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import time
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
|
||||
from mirage import MountMode, Workspace
|
||||
from mirage.resource.disk import DiskResource
|
||||
from mirage.resource.ram import RAMResource
|
||||
from mirage.types import ConsistencyPolicy
|
||||
|
||||
try:
|
||||
from mirage.resource.redis import RedisResource
|
||||
_REDIS_IMPORT_OK = True
|
||||
except ImportError:
|
||||
RedisResource = None
|
||||
_REDIS_IMPORT_OK = False
|
||||
|
||||
|
||||
def _banner(title: str) -> None:
|
||||
print(f"\n=== {title} ===")
|
||||
|
||||
|
||||
async def disk_demo() -> None:
|
||||
"""Disk has mtime fingerprints. ALWAYS detects external mutation."""
|
||||
_banner("disk + LAZY — external mutation NOT detected (cached stale)")
|
||||
lazy_root = Path(tempfile.mkdtemp(prefix="mirage-disk-lazy-"))
|
||||
try:
|
||||
(lazy_root / "file.txt").write_bytes(b"v1")
|
||||
resource = DiskResource(root=str(lazy_root))
|
||||
ws = Workspace(
|
||||
{"/data": (resource, MountMode.WRITE)},
|
||||
mode=MountMode.WRITE,
|
||||
consistency=ConsistencyPolicy.LAZY,
|
||||
)
|
||||
io1 = await ws.execute("cat /data/file.txt")
|
||||
print(f" first read (v1 expected) : "
|
||||
f"{(await io1.materialize_stdout())!r}")
|
||||
time.sleep(1.1)
|
||||
(lazy_root / "file.txt").write_bytes(b"v2-external")
|
||||
io2 = await ws.execute("cat /data/file.txt")
|
||||
print(f" second read after external write : "
|
||||
f"{(await io2.materialize_stdout())!r} <-- LAZY, stale")
|
||||
finally:
|
||||
shutil.rmtree(lazy_root, ignore_errors=True)
|
||||
|
||||
_banner("disk + ALWAYS — external mutation detected (fresh)")
|
||||
always_root = Path(tempfile.mkdtemp(prefix="mirage-disk-always-"))
|
||||
try:
|
||||
(always_root / "file.txt").write_bytes(b"v1")
|
||||
resource = DiskResource(root=str(always_root))
|
||||
ws = Workspace(
|
||||
{"/data": (resource, MountMode.WRITE)},
|
||||
mode=MountMode.WRITE,
|
||||
consistency=ConsistencyPolicy.ALWAYS,
|
||||
)
|
||||
io1 = await ws.execute("cat /data/file.txt")
|
||||
print(f" first read (v1 expected) : "
|
||||
f"{(await io1.materialize_stdout())!r}")
|
||||
time.sleep(1.1)
|
||||
(always_root / "file.txt").write_bytes(b"v2-external")
|
||||
io2 = await ws.execute("cat /data/file.txt")
|
||||
print(f" second read after external write : "
|
||||
f"{(await io2.materialize_stdout())!r} <-- ALWAYS, fresh")
|
||||
finally:
|
||||
shutil.rmtree(always_root, ignore_errors=True)
|
||||
|
||||
|
||||
async def ram_demo() -> None:
|
||||
"""RAM has no fingerprint. ALWAYS falls back to LAZY.
|
||||
|
||||
Workspace-originated writes still invalidate the cache.
|
||||
"""
|
||||
_banner("RAM + ALWAYS — no fingerprint, LAZY fallback serves stale")
|
||||
resource = RAMResource()
|
||||
resource._store.files["/file.txt"] = b"v1"
|
||||
ws = Workspace(
|
||||
{"/data": (resource, MountMode.WRITE)},
|
||||
mode=MountMode.WRITE,
|
||||
consistency=ConsistencyPolicy.ALWAYS,
|
||||
)
|
||||
io1 = await ws.execute("cat /data/file.txt")
|
||||
print(f" first read (v1 expected) : "
|
||||
f"{(await io1.materialize_stdout())!r}")
|
||||
resource._store.files["/file.txt"] = b"v2-external"
|
||||
io2 = await ws.execute("cat /data/file.txt")
|
||||
print(f" second read after external mutation : "
|
||||
f"{(await io2.materialize_stdout())!r} <-- ALWAYS→LAZY, stale")
|
||||
|
||||
_banner("RAM — workspace-originated write invalidates cache (fresh)")
|
||||
resource2 = RAMResource()
|
||||
resource2._store.files["/file.txt"] = b"v1"
|
||||
ws2 = Workspace(
|
||||
{"/data": (resource2, MountMode.WRITE)},
|
||||
mode=MountMode.WRITE,
|
||||
consistency=ConsistencyPolicy.ALWAYS,
|
||||
)
|
||||
io3 = await ws2.execute("cat /data/file.txt")
|
||||
print(f" first read (v1 expected) : "
|
||||
f"{(await io3.materialize_stdout())!r}")
|
||||
await ws2.execute('echo -n "v2-via-workspace" > /data/file.txt')
|
||||
io4 = await ws2.execute("cat /data/file.txt")
|
||||
print(f" read after workspace-owned write : "
|
||||
f"{(await io4.materialize_stdout())!r} <-- cache invalidated")
|
||||
|
||||
|
||||
async def redis_demo() -> None:
|
||||
if not _REDIS_IMPORT_OK:
|
||||
_banner("redis — SKIPPED (mirage-ai[redis] extra not installed)")
|
||||
return
|
||||
redis_url = os.environ.get("REDIS_URL", "redis://localhost:6379")
|
||||
prefix = f"mirage_consistency_demo_{uuid.uuid4().hex[:8]}"
|
||||
|
||||
_banner("redis + ALWAYS — no fingerprint, LAZY fallback serves stale")
|
||||
try:
|
||||
resource = RedisResource(url=redis_url, key_prefix=prefix)
|
||||
except Exception as exc:
|
||||
print(f" SKIPPED (could not connect to {redis_url}): {exc}")
|
||||
return
|
||||
|
||||
# Prime: write v1 through the workspace so it lands in the resource
|
||||
ws_primer = Workspace(
|
||||
{"/data": (resource, MountMode.WRITE)},
|
||||
mode=MountMode.WRITE,
|
||||
consistency=ConsistencyPolicy.LAZY,
|
||||
)
|
||||
await ws_primer.execute('echo -n "v1" > /data/file.txt')
|
||||
try:
|
||||
ws = Workspace(
|
||||
{"/data": (resource, MountMode.WRITE)},
|
||||
mode=MountMode.WRITE,
|
||||
consistency=ConsistencyPolicy.ALWAYS,
|
||||
)
|
||||
io1 = await ws.execute("cat /data/file.txt")
|
||||
print(f" first read (v1 expected) : "
|
||||
f"{(await io1.materialize_stdout())!r}")
|
||||
|
||||
# Simulate external mutation: write directly through another workspace
|
||||
# instance. The target cache (ws._cache) never sees the other write,
|
||||
# so it still serves v1 under ALWAYS (no fingerprint to compare).
|
||||
ws_other = Workspace(
|
||||
{
|
||||
"/data": (RedisResource(url=redis_url,
|
||||
key_prefix=prefix), MountMode.WRITE)
|
||||
},
|
||||
mode=MountMode.WRITE,
|
||||
)
|
||||
await ws_other.execute('echo -n "v2-external" > /data/file.txt')
|
||||
|
||||
io2 = await ws.execute("cat /data/file.txt")
|
||||
print(f" second read after external mutation : "
|
||||
f"{(await io2.materialize_stdout())!r} <-- ALWAYS→LAZY, stale")
|
||||
|
||||
# Workspace-owned write invalidates the local cache
|
||||
_banner("redis — workspace-originated write invalidates cache (fresh)")
|
||||
await ws.execute('echo -n "v3-via-workspace" > /data/file.txt')
|
||||
io3 = await ws.execute("cat /data/file.txt")
|
||||
print(f" read after workspace-owned write : "
|
||||
f"{(await io3.materialize_stdout())!r} <-- cache invalidated")
|
||||
finally:
|
||||
# Clean up keys we created
|
||||
try:
|
||||
import redis
|
||||
client = redis.Redis.from_url(redis_url)
|
||||
for key in client.scan_iter(match=f"{prefix}:*"):
|
||||
client.delete(key)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
await disk_demo()
|
||||
await ram_demo()
|
||||
await redis_demo()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,31 @@
|
||||
# ========= 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 asyncio
|
||||
|
||||
from mirage import MountMode, RAMResource, Workspace
|
||||
|
||||
ws = Workspace(
|
||||
{"/data/": RAMResource()},
|
||||
mode=MountMode.WRITE,
|
||||
)
|
||||
|
||||
print("=== curl (returns the raw page body) ===")
|
||||
result = asyncio.run(ws.execute("curl https://example.com"))
|
||||
print(result.stdout)
|
||||
|
||||
print("\n=== curl a documentation page ===")
|
||||
result = asyncio.run(
|
||||
ws.execute("curl https://docs.python.org/3/library/json.html"))
|
||||
print(result.stdout)
|
||||
@@ -0,0 +1,70 @@
|
||||
# ========= 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 asyncio
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from mirage import DiskResource, MountMode, Workspace
|
||||
from mirage.commands.config import command
|
||||
from mirage.commands.spec import SPECS
|
||||
from mirage.io.types import IOResult
|
||||
from mirage.resource.ram import RAMResource
|
||||
from mirage.types import PathSpec
|
||||
|
||||
|
||||
@command("greet", resource=["ram", "disk"], spec=SPECS["cat"])
|
||||
async def greet(
|
||||
accessor,
|
||||
paths: list[PathSpec],
|
||||
*texts: str,
|
||||
**_extra: object,
|
||||
):
|
||||
backend = type(accessor).__name__
|
||||
targets = ", ".join(p.virtual for p in paths) if paths else "(no paths)"
|
||||
body = f"hello from {backend}: {targets}\n".encode()
|
||||
return body, IOResult()
|
||||
|
||||
|
||||
async def main():
|
||||
tmp_root = Path(tempfile.mkdtemp(prefix="mirage-custom-cmd-"))
|
||||
(tmp_root / "note.txt").write_text("disk file\n")
|
||||
|
||||
ws = Workspace(
|
||||
{
|
||||
"/ram/": RAMResource(),
|
||||
"/disk/": DiskResource(str(tmp_root)),
|
||||
},
|
||||
mode=MountMode.WRITE,
|
||||
)
|
||||
|
||||
print("=== decorator-level bindings on greet ===")
|
||||
for rc in greet._registered_commands:
|
||||
print(f" resource={rc.resource!r:10} name={rc.name!r}")
|
||||
|
||||
ws.mount("/ram/").register_fns([greet])
|
||||
ws.mount("/disk/").register_fns([greet])
|
||||
|
||||
await ws.execute("echo content > /ram/note.txt")
|
||||
|
||||
print("\n=== greet on /ram/ (RAMAccessor wins) ===")
|
||||
result = await ws.execute("greet /ram/note.txt")
|
||||
print(await result.stdout_str())
|
||||
|
||||
print("=== greet on /disk/ (DiskAccessor wins) ===")
|
||||
result = await ws.execute("greet /disk/note.txt")
|
||||
print(await result.stdout_str())
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,151 @@
|
||||
# ========= 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 asyncio
|
||||
|
||||
from mirage import MountMode, RAMResource, Workspace
|
||||
|
||||
ws = Workspace(
|
||||
{"/data/": RAMResource()},
|
||||
mode=MountMode.WRITE,
|
||||
)
|
||||
|
||||
ws.dispatch("mkdir", "/data/logs")
|
||||
ws.dispatch("mkdir", "/data/src")
|
||||
APP_LOG = (b"INFO server started\nERROR disk full\n"
|
||||
b"INFO request ok\nERROR timeout\n")
|
||||
ws.dispatch("tee", "/data/logs/app.log", data=APP_LOG)
|
||||
ws.dispatch("tee",
|
||||
"/data/src/main.py",
|
||||
data=b'def main():\n print("hello")\n')
|
||||
ws.dispatch("tee",
|
||||
"/data/src/utils.py",
|
||||
data=b"def add(a, b):\n return a + b\n")
|
||||
ws.dispatch("tee", "/data/notes.txt", data=b"line one\nline two\nline three\n")
|
||||
|
||||
|
||||
async def log_result(label: str, io) -> None:
|
||||
stdout = (await io.stdout_str()).rstrip("\n")
|
||||
stderr = (await io.stderr_str()).rstrip("\n")
|
||||
code = io.exit_code
|
||||
print(f"\n{'─' * 60}")
|
||||
print(f" $ {label}")
|
||||
print(f" exit_code={code}")
|
||||
if stdout:
|
||||
for line in stdout.splitlines():
|
||||
print(f" stdout │ {line}")
|
||||
if stderr:
|
||||
for line in stderr.splitlines():
|
||||
print(f" stderr │ {line}")
|
||||
if not stdout and not stderr:
|
||||
print(" (no output)")
|
||||
|
||||
|
||||
async def main():
|
||||
print("=" * 60)
|
||||
print(" Stderr Warnings & Error Handling Demo")
|
||||
print("=" * 60)
|
||||
|
||||
# ── file not found ───────────────────────────────────────────────────
|
||||
io = await ws.execute("cat /data/nonexistent.txt")
|
||||
await log_result("cat /data/nonexistent.txt", io)
|
||||
|
||||
# ── ls on missing directory ──────────────────────────────────────────
|
||||
io = await ws.execute("ls /data/missing/")
|
||||
await log_result("ls /data/missing/", io)
|
||||
|
||||
# ── grep on missing file ─────────────────────────────────────────────
|
||||
io = await ws.execute("grep hello /data/ghost.txt")
|
||||
await log_result("grep hello /data/ghost.txt", io)
|
||||
|
||||
# ── find on missing path ─────────────────────────────────────────────
|
||||
io = await ws.execute("find /data/nowhere")
|
||||
await log_result("find /data/nowhere", io)
|
||||
|
||||
# ── recursive grep with -l (files only) on valid dir ─────────────────
|
||||
io = await ws.execute("grep -rl def /data/src")
|
||||
await log_result("grep -rl def /data/src", io)
|
||||
|
||||
# ── pipe: error in first stage ───────────────────────────────────────
|
||||
io = await ws.execute("cat /data/nonexistent.txt | head -n 1")
|
||||
await log_result("cat /data/nonexistent.txt | head -n 1", io)
|
||||
|
||||
# ── pipe: valid read, grep finds nothing ─────────────────────────────
|
||||
io = await ws.execute("cat /data/notes.txt | grep ZZZZZ")
|
||||
await log_result("cat /data/notes.txt | grep ZZZZZ", io)
|
||||
|
||||
# ── && chain: first fails → second skipped ──────────────────────────
|
||||
io = await ws.execute(
|
||||
"cat /data/nonexistent.txt && echo 'this should not print'")
|
||||
await log_result("cat /data/nonexistent.txt && echo 'should not print'",
|
||||
io)
|
||||
|
||||
# ── || chain: first fails → fallback runs ────────────────────────────
|
||||
io = await ws.execute(
|
||||
"cat /data/nonexistent.txt || echo 'fallback executed'")
|
||||
await log_result("cat /data/nonexistent.txt || echo 'fallback executed'",
|
||||
io)
|
||||
|
||||
# ── complex: (grep | sort) && echo ok || echo fail ───────────────────
|
||||
io = await ws.execute(
|
||||
"(grep ERROR /data/logs/app.log | sort) && echo ok || echo fail")
|
||||
await log_result(
|
||||
"(grep ERROR /data/logs/app.log | sort) && echo ok || echo fail", io)
|
||||
|
||||
# ── complex: same but grep finds nothing → fail path ─────────────────
|
||||
io = await ws.execute(
|
||||
"(grep ZZZZZ /data/logs/app.log | sort) && echo ok || echo fail")
|
||||
await log_result(
|
||||
"(grep ZZZZZ /data/logs/app.log | sort) && echo ok || echo fail", io)
|
||||
|
||||
# ── semicolon: independent commands, first fails ─────────────────────
|
||||
io = await ws.execute(
|
||||
"cat /data/nonexistent.txt ; cat /data/notes.txt | head -n 1")
|
||||
await log_result(
|
||||
"cat /data/nonexistent.txt ; cat /data/notes.txt | head -n 1", io)
|
||||
|
||||
# ── rm missing file (no -f) vs rm -f ────────────────────────────────
|
||||
io = await ws.execute("rm /data/nonexistent.txt")
|
||||
await log_result("rm /data/nonexistent.txt", io)
|
||||
|
||||
io = await ws.execute("rm -f /data/nonexistent.txt")
|
||||
await log_result("rm -f /data/nonexistent.txt", io)
|
||||
|
||||
# ── diff with missing file ───────────────────────────────────────────
|
||||
io = await ws.execute("diff /data/notes.txt /data/nonexistent.txt")
|
||||
await log_result("diff /data/notes.txt /data/nonexistent.txt", io)
|
||||
|
||||
# ── stat on missing file ─────────────────────────────────────────────
|
||||
io = await ws.execute("stat /data/nonexistent.txt")
|
||||
await log_result("stat /data/nonexistent.txt", io)
|
||||
|
||||
# ── tree on missing dir ──────────────────────────────────────────────
|
||||
io = await ws.execute("tree /data/nowhere")
|
||||
await log_result("tree /data/nowhere", io)
|
||||
|
||||
# ── multi-pipe success: grep | sort | head ───────────────────────────
|
||||
io = await ws.execute("grep ERROR /data/logs/app.log | sort | head -n 1")
|
||||
await log_result("grep ERROR /data/logs/app.log | sort | head -n 1", io)
|
||||
|
||||
# ── execution history: exit codes per command ─────────────────────
|
||||
print(f"\n{'═' * 60}")
|
||||
print(" Execution History")
|
||||
print(f"{'═' * 60}")
|
||||
|
||||
for entry in (await ws.history())[-6:]:
|
||||
print(f"\n $ {entry['command']}")
|
||||
print(f" exit={entry['exit_code']}")
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,65 @@
|
||||
# ========= 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 pandas as pd
|
||||
import pyarrow as pa
|
||||
import pyarrow.parquet as pq
|
||||
|
||||
from mirage import MountMode, RAMResource, Workspace
|
||||
from mirage.fuse.filetype.data.local.excel import HOOKS as EXCEL_HOOKS
|
||||
from mirage.fuse.filetype.data.local.parquet import HOOKS as PARQUET_HOOKS
|
||||
|
||||
df = pd.DataFrame({
|
||||
"name": ["alice", "bob", "charlie", "diana"],
|
||||
"score": [95, 80, 70, 88],
|
||||
"grade": ["A", "B", "C", "B+"],
|
||||
})
|
||||
|
||||
buf = io.BytesIO()
|
||||
table = pa.Table.from_pandas(df)
|
||||
pq.write_table(table, buf)
|
||||
parquet_bytes = buf.getvalue()
|
||||
|
||||
buf = io.BytesIO()
|
||||
df.to_excel(buf, index=False)
|
||||
xlsx_bytes = buf.getvalue()
|
||||
|
||||
mem = RAMResource()
|
||||
mem._store.files["/students.parquet"] = parquet_bytes
|
||||
mem._store.files["/students.xlsx"] = xlsx_bytes
|
||||
mem._store.files["/notes.txt"] = b"plain text file\n"
|
||||
|
||||
for hooks in [PARQUET_HOOKS, EXCEL_HOOKS]:
|
||||
for hook_name, fns in hooks.items():
|
||||
for fn in fns:
|
||||
RAMResource.register_fuse_hook(hook_name, fn)
|
||||
|
||||
ws = Workspace({"/": mem}, mode=MountMode.READ)
|
||||
|
||||
print("=== cat /students.parquet (raw) ===")
|
||||
raw, _ = ws.dispatch("cat", "/students.parquet")
|
||||
print(f"({len(raw)} bytes of binary data)\n")
|
||||
|
||||
print("=== fuse_read /students.parquet (hooked) ===")
|
||||
print(ws.fuse_read("/students.parquet").decode())
|
||||
|
||||
print("=== fuse_read /students.xlsx (hooked) ===")
|
||||
print(ws.fuse_read("/students.xlsx").decode())
|
||||
|
||||
print("=== fuse_read /notes.txt (no hook, passthrough) ===")
|
||||
print(ws.fuse_read("/notes.txt").decode())
|
||||
|
||||
RAMResource._fuse_hooks = {}
|
||||
Reference in New Issue
Block a user