Filesystem paths
client.path(...) returns a pathlib_next
Path object for a location on the target:
p = client.path("/mnt/tank/data/report.txt")
if p.exists():
data = p.read_bytes()
p.with_name("report.bak").write_bytes(data)
Local vs remote backends
- For a local target,
client.path(...)is an ordinary localPath. - For a remote target it is a
TruenasPath, which prefers SFTP (via thesshextra'sasyncsshbackend) and falls back to the middlewarefilesystem.*websocket API for operations SFTP doesn't cover cleanly (delete/rename/symlink).
pip install pytruenas[ssh] # enables the SFTP backend
Choose a provider for one call with client.path(..., backend="sftp"), or set
the whole preference order at construction with
path=["sftp", "tnasws"] — see Running commands.
Valid path providers are local, sftp, and tnasws.
Examples
Paths behave like pathlib.Path, so most of what you know transfers:
p = client.path("/mnt/tank/config/app.conf")
p.read_text()
p.write_text("key = value\n")
p.exists(), p.is_file(), p.stat().st_size
# Parents, joining, and globbing all work
(p.parent / "backup.conf").write_bytes(p.read_bytes())
for conf in client.path("/mnt/tank/config").glob("*.conf"):
print(conf.name, conf.stat().st_mtime)
Walking a tree and copying between hosts:
src = client.path("/mnt/tank/data")
for root, dirs, files in src.walk():
for name in files:
print(root / name)
# `pathlib_next` copies across backends, so this crosses machines
local_copy = pathlib.Path("/tmp/app.conf")
client.path("/mnt/tank/config/app.conf").copy(local_copy)
Creating directories and cleaning up:
d = client.path("/mnt/tank/scratch/run-42")
d.mkdir(parents=True, exist_ok=True)
(d / "log.txt").write_text("started\n")
d.rm(recursive=True, missing_ok=True) # pytruenas convenience
What needs SFTP
The middleware filesystem.* API has no symlink, rename, or realpath
operation, so these need the ssh extra and a reachable SSH leg. Without one
they raise NotImplementedError rather than failing silently:
| operation | sftp |
tnasws |
|---|---|---|
| read/write/stat/mkdir/chmod | ✅ | ✅ |
unlink/rmdir |
✅ | ✅ (shells out) |
rename |
✅ | ❌ |
symlink_to / readlink |
✅ | ❌ |
resolve |
✅ | returns the path unchanged |
symlink_to takes a pytruenas-specific force= that removes a conflicting
target first — a bool, a file-type string, or a set of
"file"/"link"/"directory":
link = client.path("/mnt/tank/current")
link.symlink_to("/mnt/tank/releases/v2", force="link") # replace only a symlink
Note
SFTP concurrency and transport details come from pathlib_next; pytruenas
just selects and wires the backend. The ssh extra requires
pathlib_next[sftp-async] >= 0.8.3.
See the Filesystem API.