Imported from Lightbridge-KS/pyrfs (
AGENTS.md). Install upstream withnpx skills add Lightbridge-KS/pyrfs. Copyright stays with the author.
pyrfs — Agent Instructions
Pythonic port of R's fs UX: tidy paths, typed values
(FsPath/Bytes/Perms), chainable, pandas-friendly. Pure Python ≥ 3.10, stdlib-only core.
Design docs (read before structural changes): docs/design/pyrfs-ux.md (API feel),
docs/design/pyrfs-architecture.md (structure).
README is generated: never edit README.md directly — edit README.qmd, then render
with uv run --group docs --extra pandas quarto render README.qmd (Quarto executes the
code cells; temp paths are kept deterministic via file_temp_push). Commit both files.
Docs site: MkDocs Material (mkdocs.yml; content in docs/), deployed on Netlify
(https://pyrfs.netlify.app). Build locally with
uv run --group docs --extra pandas mkdocs build --strict — the tour notebook executes
during the build (it is symlinked from examples/ into docs/tour/). API pages use
mkdocstrings ::: pyrfs.<name> directives; new public API needs an entry in docs/api/.
Architecture invariants
- One engine, three surfaces. All logic lives in
pyrfs/_engine/; the functional API,FsPathmethods, and the pandas.fsaccessor are thin delegates. Never duplicate logic in a surface. pyrfs/_engine/,values.py,display.pynever import pandas (not even indirectly).pyrfs/_pandas/depends inward on them, never the reverse. Optional registration happens inpyrfs/__init__.pyviacontextlib.suppress(ImportError).- Engine
*_infoalways returnslist[dict]; the public surface (pyrfs/info.py) upgrades to a typed DataFrame when pandas is present. display.pyis the single source of truth for parse/format (bytes, perms, tidy, colour).- Layout is flat: the package is
pyrfs/at repo root (nosrc/).
Commands
Workflow is orchestrated by the Makefile — make help lists everything:
make gate # the full gate suite: lint, format-check, typecheck,
# test (pandas), test-core, docs build, readme-check
make test # pytest with the pandas extra
make test-core # pytest without pandas (prunes the venv first)
make docs-serve # live-preview the docs site
make readme # re-render README.md from README.qmd
Run make gate before any commit. Caution: uv run --extra pandas leaves pandas in
.venv; a plain uv run pytest afterwards is NOT a core-mode run — make test-core
handles the pruning for you.
Conventions
mypy --strict, noAnyin the core package. pandas/numpy are opaque to mypy (follow_imports = "skip"in pyproject) so results are identical with/without the extra; onlypyrfs._pandas.*has narrow relaxations.- NumPy-style docstrings on public API; public methods before private in classes.
- fs argument conventions: behavior flags keyword-only; safe defaults
(
overwrite=False,recurse=Falsefor listing /Truefordir_create,all=False,fail=True);glob/regexpmutually exclusive →FsValueError. - Errors: native
OSErrorsubclasses for OS failures;FsError/FsValueErrorfor pyrfs-level validation only. - Conventional Commits, committed directly to
main(early phase); CI must be green.
Releases
The version's single source of truth is pyproject.toml (bump with
uv version --bump …; pyrfs.__version__ reads it via importlib.metadata).
To release: bump version → move the CHANGELOG [Unreleased] content under a
new ## [X.Y.Z] - date heading (keep an empty [Unreleased], update the
link refs) → make gate → commit/push → git tag vX.Y.Z && git push --tags.
The tag triggers .github/workflows/release.yml: gate + tag/version guard →
build → TestPyPI via Trusted Publishing (environment testpypi, no tokens) →
draft GitHub release with changelog-derived notes (a human publishes it).
Semantics worth knowing (deliberate, don't "fix")
FsPathsubclassesstr— never shadowstrmethods (the split method isparts())./join is concatenation + tidy (an absolute RHS does not reset the path, unlikeos.path.join).Bytesunits are all 1024-based ("10MB"=="10MiB"), matching R fs.is_file/is_diruse lstat semantics: a symlink is onlyis_link.file_existsuseslexists(broken symlinks count). Nodir_move— dirs move viafile_move.dir_copy/file_copyinto an existing dir resolve todir/basenamebefore the overwrite guard.- Colour (
FsPath.__repr__,dir_tree):NO_COLOR>FORCE_COLOR> TTY detection.