Skip to main content

Python bindings for rtrash — FreeDesktop trash as a safe alternative to os.remove

Project description

rtrash

rtrash logo

Native Rust FreeDesktop trash for Linux: an rm-compatible put path and the everyday trash-cli suite (trash-put, trash-empty, trash-list, trash-restore, trash-rm) as one multi-call binary. Implements the freedesktop.org Trash specification. No interpreter startup; emptying deletes entries in parallel via rayon.

Documentation

Doc Contents
docs/getting-started.md Install CLI + Python, shortest paths
docs/architecture.md FreeDesktop layout, safety vs rm / os.remove / trash-cli
docs/benchmarks.md Measured safer/better/faster vs trash-cli
docs/bindings.md Maturin/PyO3 API (unlink / rmtree replacements)

Org-mode sources (readcon-core style): docs/orgmode/.

Python: replace permanent delete

import rtrash
rtrash.unlink("file.txt")    # not os.remove
rtrash.rmtree("build/")      # not shutil.rmtree
$ pip install maturin && maturin develop --features python

Docs site

Published at https://rtrash.rgoswami.me (GitHub Pages).

Project documentation is authored in orgmode under docs/orgmode/ and built to a Sphinx + Shibuya static site (readcon-style: org → RST → HTML).

# needs: pandoc (or emacs + ox-rst), python3, venv
./docs/build.sh
# HTML → docs/build/index.html
python3 -m http.server -d docs/build 8000
Page Source
Landing docs/orgmode/index.org
Getting started docs/orgmode/getting-started.org
Architecture docs/orgmode/architecture.org
Benchmarks docs/orgmode/benchmarks.org
Python bindings docs/orgmode/bindings.org

Markdown mirrors under docs/*.md remain for quick GitHub reading; the site is the structured presentation of the same material.

Platform

Platform: Linux FreeDesktop trash only (home trash + per-mount trash dirs). Not a Windows/macOS system-trash wrapper, and not a colored TUI.

Install

Recommended order when a GitHub Release for this version exists (tag v0.1.0 style). Prebuilt Linux musl static assets:

Arch Asset basename
x86_64 rtrash-<version>-x86_64-unknown-linux-musl.tar.gz
aarch64 (ARM64) rtrash-<version>-aarch64-unknown-linux-musl.tar.gz

Naming matches [package.metadata.binstall] in Cargo.toml / scripts/package-release.sh:

1. cargo binstall (preferred binary install)

Uses cargo-binstall to download the prebuilt Linux musl static tarball for your CPU (no local compile). CI ships x86_64 and aarch64 musl assets. Crate metadata remaps typical glibc hosts (*-unknown-linux-gnu) to the matching musl tarball, so a normal:

$ cargo binstall rtrash

does not look for a non-existent *-linux-gnu*.tar.gz on x86_64 or aarch64 Linux. You do not need --pkg-url or --target on those hosts. Requires a published GitHub Release for this version (tag v*).

# one-time: install cargo-binstall itself (see upstream docs)
$ cargo binstall cargo-binstall

# once this version has a GitHub Release asset (x86_64 or aarch64 Linux):
$ cargo binstall rtrash
# or before crates.io publish, from this repo's metadata:
$ cargo binstall --git https://github.com/HaoZeke/rtrash rtrash

$ rtrash setup

macOS and Windows have no prebuilt FreeDesktop assets (and Windows system trash is out of scope for this tool's niche). Use from-source only where the Linux FreeDesktop code path applies, or track the open port work separately.

Always run rtrash setup after the binary lands on PATH so multi-call links, shell completions (bash/zsh/fish), and the man page are installed under ~/.local (override with --prefix=DIR).

2. Manual musl tarball (no cargo at all)

When a Release exists, download the matching asset from the repository Releases page, extract, put bin/ on PATH, then rtrash setup --force (or follow INSTALL.txt in the tarball).

Build the same artifact yourself (builder host with musl target):

$ ./scripts/package-release.sh x86_64-unknown-linux-musl
$ ./scripts/package-release.sh aarch64-unknown-linux-musl
# or, on a host that can build both:
$ ./scripts/package-release.sh --all
#  dist/rtrash-<version>-{x86_64,aarch64}-unknown-linux-musl.tar.gz

CI: .github/workflows/release.yml builds both targets on v* tags (x86_64 and ubuntu-24.04-arm) and attaches the tarballs to the GitHub Release (what binstall downloads).

3. From source (cargo install)

Requires a Rust toolchain (MSRV 1.77). Dynamically linked glibc binary by default.

$ cargo install --git https://github.com/HaoZeke/rtrash
$ rtrash setup

What rtrash setup installs

What Where (default)
Multi-call symlinks (trash-put, trash-empty, trash-list, trash-restore, trash-rm, trash) ~/.local/bin/ → this rtrash
bash completion ~/.local/share/bash-completion/completions/rtrash (+ multi-call links)
zsh completion ~/.local/share/zsh/site-functions/_rtrash
fish completion ~/.config/fish/completions/rtrash.fish (+ multi-call *.fish links)
man page ~/.local/share/man/man1/rtrash.1

Useful flags: rtrash setup --dry-run, --force (refresh after upgrade), --with-rm (also link rm → put into trash), --prefix=/usr/local.

Subcommands work without multi-call names: rtrash put|empty|list|status|restore|rm ….

Multi-call name Meaning
rm / trash / trash-put put (move paths into the trash)
trash-list list
trash-restore restore
trash-empty empty
trash-rm permanently delete matching trash entries

Subcommand rtrash rm PATTERN is the same as multi-call trash-rm (not the same as multi-call rm, which puts).

Packagers and custom layouts

Sources also live in-tree for packaging (completions/, man/rtrash.1). From any installed binary:

$ rtrash completions bash > rtrash.bash
$ rtrash completions zsh  > _rtrash
$ rtrash completions fish > rtrash.fish
$ rtrash man              > rtrash.1
$ rtrash setup --prefix=/usr --force    # system prefix when packaging

Tutorial

Trash a file, inspect the trash, restore it, then empty:

$ echo data > scratch.txt
$ rtrash scratch.txt
$ rtrash list
2026-07-03 14:00:00 /home/you/scratch.txt
$ rtrash restore scratch.txt
restored '/home/you/scratch.txt'
$ rtrash scratch.txt
$ rtrash empty
Removed 1 item

Selective permanent delete from the trash (trash-cli trash-rm):

$ rtrash put a.o b.c
$ rtrash rm '*.o'          # quote globs; removes a.o from trash only
$ rtrash list              # b.c still listed

rtrash FILE with no subcommand behaves like rm, so shell habits carry over: rtrash -rf build/ moves build/ to the trash instead of unlinking it.

Scripts / tests: pin with --trash-dir=… and/or isolate XDG_DATA_HOME so list/empty/restore/rm do not walk every mount’s trash.

Reference

rtrash put (also rm, trash, trash-put, or bare rtrash)

Accepts the common rm(1) flags with the same semantics, except that files move to the trash: -f, -i, -I, --interactive[=WHEN], -r/-R, -d, -v, --one-file-system (accepted for rm compatibility; no-op because entries move whole and are never walked like rm -x), --preserve-root (default), --no-preserve-root, --. Directories need -r (or -d when empty), ./..// are refused (unless --no-preserve-root for /), and exit codes mirror rm (0 success, 1 failure, 2 usage error).

As with GNU rm, the last of -f / -i / -I (and the matching long forms) wins for prompt vs force behavior. -f also ignores missing paths.

Trash placement follows the spec: the home trash ($XDG_DATA_HOME/Trash, defaulting under ~/.local/share) for same-filesystem files, $top/.Trash/$uid (must be a sticky non-symlink directory) or $top/.Trash-$uid on other mounts, with a copy-into-home-trash fallback when the volume cannot host a trash directory. Names are reserved atomically (create-new / O_EXCL on the .trashinfo), so concurrent invocations never clobber each other; collisions get name.2, name.3, ...

Putting a directory updates that trash dir’s FreeDesktop directorysizes cache (size mtime percent-encoded-name). Putting ordinary files does not add directory lines.

rtrash empty [DAYS] (also trash-empty)

Purges every trash directory visible to the user (home trash plus mounted volumes). With DAYS, only items trashed more than DAYS days ago go. Entries are removed in parallel. Orphaned files/ entries (no .trashinfo) and entries with broken metadata are purged on a full empty, and the directorysizes cache is pruned when present. Options: -n/--dry-run (also prints an approximate reclaimable size via a fast in-process walk of the victims, like a small du of what would go away), -v/--verbose, --trash-dir=PATH (repeatable), -f (accepted for trash-cli compatibility; emptying never prompts).

rtrash status

Prints per-trash-root and total item count plus approximate reclaimable size (same disk-usage walk as empty dry-run). Accepts --home-only and --trash-dir.

rtrash list (also trash-list)

Prints DELETION-DATE ORIGINAL-PATH per item, oldest first, in the trash-list output format (YYYY-MM-DD HH:MM:SS plus the original path). Scans the home trash and per-mount trash directories owned by the current user, or only the directories given with --trash-dir=PATH (repeatable).

rtrash restore [PATH] (also trash-restore)

Restores the item trashed from PATH (or picks among items trashed from under the current directory when PATH is omitted). A single match restores directly; multiple matches list with indices for interactive selection (requires a TTY on stdin). Existing paths at the destination are preserved unless -f / --force is given; with -f, a blocking destination is removed first. Same-filesystem restore uses rename; cross-device restore copies then deletes the trash payload (needed when put fell back to the home trash). Options: --trash-dir=PATH (repeatable).

rtrash rm PATTERN... (also trash-rm)

Permanently deletes trash entries whose original path, basename, or trash name matches a shell-style glob PATTERN (quote globs from the shell). Matching files/ payloads and .trashinfo files are removed; non-matches stay. Does not restore. Options: -v/--verbose, --trash-dir=PATH (repeatable).

FreeDesktop durability notes

Previously deferred items are implemented in the shipped put/empty path:

  • Durable .trashinfo: put fsyncs the reserved info file (and best-effort the info/ dir) before moving the payload.
  • EXDEV fidelity: cross-device put/restore copies preserve content, symlink-as-link, mode, and mtime (not a bare content-only copy).
  • Put/empty exclusion: each trash root takes an exclusive flock on .rtrash.lock for put and empty so the pair cannot tear mid-operation.
  • Btrfs multi-subvol topdir: volume topdir is the longest mount-point prefix from /proc/self/mounts, not a pure st_dev parent walk.
  • Default multi-volume empty: with no --trash-dir, empty/list/restore/rm discover home trash plus existing user trash on every non-pseudo mount (including /), matching trash-cli’s multi-volume default. Pin with --trash-dir in scripts.

Limitations and non-goals

  • No pretty TUI: no colored tables or fuzzy restore (see trashy/gtrash).
  • Linux FreeDesktop only: no Windows Recycle Bin / macOS Trash backends.
  • Not a general soft-delete database: only the FreeDesktop on-disk layout.
  • EXDEV does not re-create xattrs/ACLs/hardlinks (mode+mtime+symlink+bytes only).
  • Locks are local flock (advisory on some network FS).

Performance

Historical single-machine comparison against trash-cli 0.24.5.26 (CPython 3) on an NVMe-backed Linux host, best of warm runs. These numbers are not continuously re-verified in CI and will vary by filesystem, core count, and trash layout; treat them as order-of-magnitude evidence that native startup and parallel empty help, not as a live leaderboard.

Operation trash-cli rtrash ratio
empty, 100 000 entries 0.92 s 0.36 s 2.5x
empty, 20 000 entries 0.21 s 0.07 s 3x
put, one file 52 ms 1 ms 52x

Full empty was further optimized for large trashcans: no pre-scan when not verbose, d_type-fast unlinkat for regular files, and serial wipes of the files/ then info/ roots with parallel children inside each. Emptying uses an in-process bulk tree delete (readdir/unlinkat walk inspired by empty-source rsync --delete, not a shell-out to rsync). On btrfs, if a trash payload is a real subvolume root, empty uses BTRFS_IOC_SNAP_DESTROY instead of walking the tree. Interactive put mainly wins on process startup versus trash-cli.

Development

$ cargo test

Integration tests isolate trash under a temporary XDG_DATA_HOME and pin empty / list / restore / rm with --trash-dir=… so they never clear the host trash.

Citation

@software{rtrash,
  author = {Goswami, Rohit},
  title  = {rtrash: a fast rm-compatible freedesktop.org trash tool},
  url    = {https://github.com/HaoZeke/rtrash},
  year   = {2026}
}

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

rtrash-0.1.0-cp314-cp314-manylinux_2_34_x86_64.whl (317.0 kB view details)

Uploaded CPython 3.14manylinux: glibc 2.34+ x86-64

rtrash-0.1.0-cp313-cp313-manylinux_2_34_x86_64.whl (317.0 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.34+ x86-64

rtrash-0.1.0-cp312-cp312-manylinux_2_34_x86_64.whl (317.4 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.34+ x86-64

rtrash-0.1.0-cp311-cp311-manylinux_2_34_x86_64.whl (318.1 kB view details)

Uploaded CPython 3.11manylinux: glibc 2.34+ x86-64

rtrash-0.1.0-cp310-cp310-manylinux_2_34_x86_64.whl (318.4 kB view details)

Uploaded CPython 3.10manylinux: glibc 2.34+ x86-64

File details

Details for the file rtrash-0.1.0-cp314-cp314-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for rtrash-0.1.0-cp314-cp314-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 38be32e6426b464cab91c72d97ef9db467670ad52fed4d62a738626a9e6ea68e
MD5 15917ed5928deb355f10df2395c90422
BLAKE2b-256 0386f6aaec38732ecd50680d366176056f79c5eb6717a3e4aa45bf2eaf47178f

See more details on using hashes here.

File details

Details for the file rtrash-0.1.0-cp313-cp313-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for rtrash-0.1.0-cp313-cp313-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 80913171c03554229afbd3af7a395244f78ca0d73300f503c2af2a3e397dd745
MD5 7495dcf1b3f34e184b88883a97703682
BLAKE2b-256 b8877d2085d1582cc483374c05bd1991ad3ae501c71bc3e3940b4cc53644f4b1

See more details on using hashes here.

File details

Details for the file rtrash-0.1.0-cp312-cp312-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for rtrash-0.1.0-cp312-cp312-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 4c6068fe5b73354b8d86c443ca06e6f32f11413008d1f4702d865d8de2e9d212
MD5 c37d339939d50f45f1adcfe07db8433d
BLAKE2b-256 94842598c1cf2c4da53dc038ff3f1037daf15e9a0b9bd7aec43156d9fb6ff32f

See more details on using hashes here.

File details

Details for the file rtrash-0.1.0-cp311-cp311-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for rtrash-0.1.0-cp311-cp311-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 c5c7dee3ace40b5416d1d9d60f1c128fcc43d17996aaa347c5a727adaf466b50
MD5 5fbe4d255ca497a9035598233160e24c
BLAKE2b-256 874a2320f817532d577e448a92b3e24397beee1b32dcba4344d43944b3559cc6

See more details on using hashes here.

File details

Details for the file rtrash-0.1.0-cp310-cp310-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for rtrash-0.1.0-cp310-cp310-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 1ffe137a6acbc104802ce9fd31d1e38c085493b867b71f343a3017edc774ba71
MD5 11a2c0e250e82ca65bd53cc8b41e8d9e
BLAKE2b-256 f597c94baa0ae7e169175c8e404c0cee3563e6d16f7430cf65469c35adfc18c2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page