Metadata-Version: 2.4
Name: monkeyfs
Version: 0.1.3
Summary: Transparent filesystem interception via monkey-patching.
Author: ashenfad
License-Expression: MIT
Project-URL: Homepage, https://github.com/ashenfad/monkeyfs
Project-URL: Bug Tracker, https://github.com/ashenfad/monkeyfs/issues
Project-URL: Documentation, https://github.com/ashenfad/monkeyfs#readme
Project-URL: Source, https://github.com/ashenfad/monkeyfs
Keywords: filesystem,monkey-patch,sandbox,vfs,interception
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Filesystems
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: ruff; extra == "dev"
Requires-Dist: pre-commit; extra == "dev"
Requires-Dist: pytest; extra == "dev"
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Dynamic: license-file

# monkeyfs 🐒

Filesystem interception via monkey-patching.

Patches `open()`, `os.listdir()`, `os.stat()`, and 30+ other stdlib functions to route through a virtual or isolated filesystem. Patches are applied lazily on first `patch()` call and are inert outside the context. Uses `contextvars` for async-safe isolation between concurrent tasks. Zero dependencies.

## Install

```bash
pip install monkeyfs
```

## Quick example

```python
from monkeyfs import VirtualFS, patch

vfs = VirtualFS({})

with patch(vfs):
    with open("data.csv", "w") as f:
        f.write("name,score\nalice,98\nbob,87\n")

    import os
    print(os.listdir("/"))        # ['data.csv']
    print(os.path.getsize("data.csv"))  # 30

    with open("data.csv") as f:
        print(f.read())           # name,score\nalice,98\nbob,87\n
```

## IsolatedFS

Restricts file operations to a root directory on the real filesystem:

```python
from monkeyfs import IsolatedFS, patch

isolated = IsolatedFS(root="/tmp/sandbox")

with patch(isolated):
    with open("notes.txt", "w") as f:
        f.write("hello")          # Written to /tmp/sandbox/notes.txt

    open("/etc/passwd")           # PermissionError -- outside root
```

## ReadOnlyFS

Wraps any filesystem and blocks all write operations:

```python
from monkeyfs import VirtualFS, ReadOnlyFS, patch

vfs = VirtualFS({})
vfs.write("data.csv", b"a,b,c")

ro = ReadOnlyFS(vfs)
with patch(ro):
    print(open("data.csv").read())  # a,b,c
    open("new.txt", "w")           # PermissionError
```

## MountFS

Routes operations to different filesystems by path prefix:

```python
from monkeyfs import VirtualFS, MountFS, ReadOnlyFS, patch

base = VirtualFS({})
overlay = VirtualFS({})
overlay.write("summary.md", b"# Chapter 1")

fs = MountFS(base, {"/chapters": ReadOnlyFS(overlay)})

with patch(fs):
    # Writes go to base
    with open("app.py", "w") as f:
        f.write("print('hi')")

    # Reads from /chapters go to the overlay
    print(open("/chapters/summary.md").read())  # # Chapter 1

    # Writes to /chapters are blocked (read-only)
    open("/chapters/new.md", "w")  # PermissionError
```

## Part of the agex stack

monkeyfs provides filesystem interception for [sandtrap](https://github.com/ashenfad/sandtrap) and [agex](https://github.com/ashenfad/agex), giving sandboxed agent code an isolated virtual filesystem. `VirtualFS` accepts any dict-like backing store -- including [kvgit](https://github.com/ashenfad/kvgit) `Staged` instances for a versioned filesystem with commit/rollback.

## Documentation

- [API Reference](docs/api.md) -- public API, FileSystem protocol, patched functions

## Development

```bash
uv sync --extra dev
uv run pytest
```

## License

MIT
