Cross-platform file system utilities for Python
Project description
zerofilesystem
Cross-platform file system utilities for Python 3.12+. Zero runtime dependencies.
Problem
Reliable file handling in Python normally means stitching together os, shutil, pathlib, json, gzip, tarfile, zipfile, hashlib, fcntl/msvcrt, tempfile and secrets, then carefully wrapping each operation to make it atomic, cross-platform, and crash-safe. The pain shows up in three recurring places: writes that can be left half-finished if the process dies, multi-file operations that have no rollback when one of them fails, and platform-specific behavior — file locking, hidden attributes, executable bits, path normalization — that has to be re-derived in every project.
The cost of getting these right is rarely the headline feature, but the cost of getting them wrong is corrupted config files, partial deploys, and "works on Linux, breaks on Windows" bug reports.
Solution
zerofilesystem consolidates these primitives behind a single, flat API and makes safe behavior the default. Atomic writes are on by default (temp file plus os.replace). FileTransaction groups multiple writes/copies/deletes under a single commit-or-rollback umbrella. FileLock exposes one cross-platform locking interface backed by fcntl on Unix/macOS and msvcrt on Windows. Finder and Watcher provide fluent builder APIs for filtered search and polling-based monitoring. Archive extraction guards against zip-slip path traversal. Every public function accepts both str and pathlib.Path.
The library is built on the standard library only — no external runtime dependencies — and ships a py.typed marker for full PEP 561 typing.
What it gives you
- Atomic text/binary/JSON writes — temp file + atomic rename, on by default.
zfs.write_text("config.json", data). - Multi-file transactions — commit several writes/copies/deletes together, automatic rollback on error.
with zfs.FileTransaction() as tx: .... - Cross-platform file locking — same interface on Unix and Windows, with optional timeout.
with zfs.FileLock("/tmp/app.lock", timeout=5): .... - Fluent file finder — patterns, exclusions, size/date/permission filters, depth limits.
Finder("./src").patterns("*.py").modified_last_days(7).find(). - Polling file watcher — created/modified/deleted callbacks with debouncing and the same filter vocabulary as
Finder. - Integrity verification —
directory_hash,create_manifest/verify_manifest,compare_directories,snapshot_hash. - Archives — tar (gz/bz2/xz) and zip create/extract with filtering, base-dir control, and zip-slip protection.
- Secure operations —
secure_delete(multi-pass overwrite),private_directory(0o700),create_private_file(0o600). - Path utilities — normalize, expand
~/env vars, validate, posix conversion, subpath checks. - Permissions and metadata —
FileMetadatadataclass, readonly/hidden/executable toggles, octal-mode parsing.
Installation
pip install zerofilesystem
uv add zerofilesystem
Requires Python 3.12+. No runtime dependencies.
Quick start
import zerofilesystem as zfs
# Atomic JSON write
zfs.write_json("config/app.json", {"version": 2})
# Multi-file transaction with automatic rollback
with zfs.FileTransaction() as tx:
tx.write_text("config/app.json", '{"version": 3}')
tx.write_text("config/db.json", '{"host": "localhost"}')
# Both files committed atomically on exit
# Cross-platform locking
with zfs.FileLock("/tmp/myapp.lock", timeout=5):
# Critical section — held across processes
...
# Fluent file search
from zerofilesystem import Finder
recent_py = (
Finder("./src")
.patterns("*.py")
.modified_last_days(7)
.not_hidden()
.exclude("__pycache__")
.find()
)
# Integrity manifest
manifest = zfs.create_manifest("./src", algorithm="sha256")
zfs.save_manifest(manifest, "src.manifest")
result = zfs.verify_manifest("./src", manifest)
assert result.is_valid
More runnable examples in examples/.
Comparison with alternatives
The standard library provides every primitive zerofilesystem uses; the value is in combining them safely under one API. Third-party libraries cover individual slices.
| Library | Atomic writes | Transactions | Cross-platform lock | Fluent finder | File watcher | Integrity manifest | Archives | Runtime deps |
|---|---|---|---|---|---|---|---|---|
| zerofilesystem | ✓ | ✓ | ✓ | ✓ | polling | ✓ | tar+zip | none |
pathlib + shutil (stdlib) |
manual | — | — | basic glob | — | manual | tar+zip | none |
filelock |
— | — | ✓ | — | — | — | — | none |
portalocker |
— | — | ✓ | — | — | — | — | none |
watchdog |
— | — | — | — | native FSEvents/inotify | — | — | yes |
send2trash |
— | — | — | — | — | — | — | varies |
aiofiles |
— | — | — | — | — | — | — | yes |
watchdog is the right choice when you need real-time filesystem events; zerofilesystem's Watcher is a polling implementation and trades latency for portability and zero dependencies.
Known limits and open issues
These are derived from the code and are not blockers for normal use; pick the library only if they fit your scenario.
- limit:
WatcherandFileWatcherpoll the filesystem (time.sleep(poll_interval)) — there is no inotify/FSEvents/ReadDirectoryChangesW backend, so events are bounded by the configured poll interval (default 1.0s). - limit:
secure_deleteis best-effort — modern SSDs with wear leveling, journaling filesystems, and OS-level page cache may retain copies. The docstring says so explicitly. Use full-disk encryption for true confidentiality. - limit:
move_if_absentis documented as non-atomic — there is a race window between theexists()check andshutil.move(). UseFileLockif you need cross-process exclusivity. - limit:
copy_if_neweruses a 1-second epsilon onst_mtimeto tolerate filesystem timestamp granularity, so sub-second updates can be missed. - limit:
FilePermissions.set_hidden()raisesNotImplementedErroron Unix — hiding a file there requires renaming with a leading dot. - limit: archive extraction silently skips members that would escape the destination (path traversal protection) instead of raising — call
list_archivefirst if you need to verify contents. - open:
CHANGELOG.mdcurrently stops at0.1.1whilesrc/zerofilesystem/__init__.pydeclares__version__ = "0.1.3"— release-please should reconcile this on the next release. - design: no
async/awaitvariants — every operation is blocking. Wrap in a thread executor if you need to drive I/O from an event loop.
A deeper, per-component breakdown lives outside the public docs.
Anti-patterns — how NOT to use this project
A short list of misuses that the library does not protect you from.
- Do not use
FileLockto coordinate threads inside one process — it is a process-level OS lock; usethreading.Lockfor intra-process synchronization. - Do not call
secure_deleteand assume the data is unrecoverable on SSD or any journaling filesystem — see Known limits. - Do not rely on
move_if_absent(on_conflict="skip")for atomic "move only if missing" — combine it withFileLockif more than one process can race. - Do not share one
FileTransactioninstance between threads or callcommit()more than once — a committed/rolled-back transaction refuses further operations. - Do not start a
Watcherand forget about it — it spawns a daemon thread; call.stop()or use thewithblock. - Do not use
Finder/Watcherwithpoll_fast()(0.1s) on a tree of tens of thousands of files — every tick re-walks the tree and re-stats every match. - Do not pass mismatched algorithms to
verify_manifest— the manifest stores its algorithm; pass the value returned byload_manifestinstead of hardcoding. - Do not extract an archive into a directory that contains files you care about — extraction overwrites colliding paths without prompting.
Running tests
uv sync
uv run pytest
The suite collects over 500 tests across every public module — archives, finder, watcher, file locks, transactions, JSON, path utils, basic I/O, directory ops, integrity, secure ops, permissions — and is pinned at 100% coverage in pyproject.toml.
Running examples
Each script under examples/ is self-contained and runnable:
uv run python examples/01_basic_io.py
uv run python examples/09_finder.py
uv run python examples/10_watcher.py
See examples/ for the full list (basic I/O, JSON, discovery, locking, transactions, archives, directory ops, finder, watcher).
Development
Contributor setup, lint/type/test commands, pre-commit hooks, commit conventions, and the release process are documented in docs/DEVELOPMENT.md.
Documentation map
User documentation:
- README.md — this file.
- docs/USER_GUIDE.md — extended self-sufficient user guide.
- docs/ANTI_PATTERNS.md — how NOT to use the library, with the correct alternatives.
Developer documentation:
- docs/DEVELOPMENT.md — contributor setup, tooling, release process.
- docs/ARCHITECTURE.md — modules, dependency graph, design decisions.
- docs/API_REFERENCE.md — every public symbol, verbatim signatures.
Contributing
This repository is maintained as a personal portfolio project. Pull requests are generally not accepted, but exceptional contributions may be considered.
For bug reports and feature requests, please use GitHub Issues.
License
MIT License — Copyright (c) 2025 Francesco Favi.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file zerofilesystem-0.1.4.tar.gz.
File metadata
- Download URL: zerofilesystem-0.1.4.tar.gz
- Upload date:
- Size: 331.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ce92dc42a6036361d5791391ba6fa581b4551820895ee8c0d2d9e53d7f24a31c
|
|
| MD5 |
fadafdc6ff87023da8abd55d9d5999a2
|
|
| BLAKE2b-256 |
70c368d2a8ad51eef37dbb87e9d4b9c4a17d923fc02e36d5c31393e831f223f8
|
Provenance
The following attestation bundles were made for zerofilesystem-0.1.4.tar.gz:
Publisher:
publish.yml on francescofavi/zerofilesystem
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zerofilesystem-0.1.4.tar.gz -
Subject digest:
ce92dc42a6036361d5791391ba6fa581b4551820895ee8c0d2d9e53d7f24a31c - Sigstore transparency entry: 1395334056
- Sigstore integration time:
-
Permalink:
francescofavi/zerofilesystem@de9e3da2ab87050ce3706a84195cafe07f474920 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/francescofavi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@de9e3da2ab87050ce3706a84195cafe07f474920 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file zerofilesystem-0.1.4-py3-none-any.whl.
File metadata
- Download URL: zerofilesystem-0.1.4-py3-none-any.whl
- Upload date:
- Size: 56.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2c2e7da9363b3c034bbf2bf8bb4b3c1b91a339a22f3f32a14b74541585614b94
|
|
| MD5 |
30d97cbfa7ebf75b91a7bded3f77fa0f
|
|
| BLAKE2b-256 |
c3ebd6e15d61ffb4b32f185e14248e34790197e7993109fca80f8bea0cc471b3
|
Provenance
The following attestation bundles were made for zerofilesystem-0.1.4-py3-none-any.whl:
Publisher:
publish.yml on francescofavi/zerofilesystem
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zerofilesystem-0.1.4-py3-none-any.whl -
Subject digest:
2c2e7da9363b3c034bbf2bf8bb4b3c1b91a339a22f3f32a14b74541585614b94 - Sigstore transparency entry: 1395334116
- Sigstore integration time:
-
Permalink:
francescofavi/zerofilesystem@de9e3da2ab87050ce3706a84195cafe07f474920 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/francescofavi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@de9e3da2ab87050ce3706a84195cafe07f474920 -
Trigger Event:
workflow_dispatch
-
Statement type: