Rclone for Python
🚀 A robust, typed Python wrapper for rclone.
It shells out to the rclone binary the safe way — every command is built as an
argument list and run without a shell, so paths and remote specs are
never re-interpreted. Common subcommands have explicit, typed methods that
return structured results; anything else is one run() call away.
Highlights
- 🔒 No shell, no injection. Arguments are passed as a list — never a shell string.
- 🧱 Zero required dependencies. Pure standard library.
tqdmis optional, only for progress bars. - 🧩 Typed API + structured results.
lsjson→list[dict],size→SizeResult, transfers →RcloneResult. - 📊 Reliable progress. Driven by rclone's own JSON stats log (works for remote destinations too), not by guessing file sizes.
- 💥 Real errors. Non-zero exits raise
RcloneCommandErrorcarrying the exit code, its meaning, and captured output.
Requirements
- 🐍 Python >= 3.8
- 📦 rclone on your
PATH(or passbinary=.../ setRCLONE_BINARY)
Installation
pip install rclone # core, no third-party dependencies
pip install "rclone[progress]" # adds tqdm for progress bars
Quick start
from rclone import Rclone
rc = Rclone() # finds rclone on PATH
rc.copy("foo.txt", "remote:/path/to/dst")
rc.ls("remote:/path/to/dir") # ['foo.bin', 'bar.txt', 'foo/']
rc.size("remote:/path/to/dir") # SizeResult(count=5, bytes=170397, sizeless=0)
The legacy import path still works:
from rclone.rclone import Rclone.
Examples
Transfers with progress
# A tqdm progress bar (requires the `progress` extra):
rc.copy("bigfile.bin", "remote:/backup", progress=True)
# Or your own callback, called on every stats tick:
def on_progress(stats):
print(f"{stats.percentage}% {stats.bytes}/{stats.total_bytes} {stats.speed:.0f} B/s")
result = rc.move("data/", "remote:/archive", progress=on_progress)
print(result.success, result.stats.bytes)
Listing and info
rc.ls("remote:/dir") # ['bar.txt', 'foo/'] (names only)
rc.lsjson("remote:/dir") # [{'Path': 'bar.txt', 'Name': 'bar.txt', 'Size': 0, 'IsDir': False}, ...]
# lsd (directories) and lsl (files) return structured Entry objects:
rc.lsd("remote:/dir") # [Entry(name='sub', size=128, mod_time=datetime(...), is_dir=True, count=-1), ...]
rc.lsl("remote:/dir") # [Entry(name='bar.txt', size=11, mod_time=datetime(...), is_dir=False), ...]
for entry in rc.lsd("remote:/dir"):
print(entry.name, entry.size, entry.mod_time)
size = rc.size("remote:/dir")
size.count, size.bytes # also: size.total_objects, size.total_size (legacy aliases)
rc.about("remote:") # {'total': ..., 'used': ..., 'free': ...}
rc.md5sum("remote:/dir") # {'bar.txt': 'd41d8cd9...'}
rc.listremotes() # ['gdrive:', 's3:']
rc.version() # 'v1.74.3'
Extra flags
Pass any rclone flags as positional arguments to any method:
rc.ls("remote:/dir", "-R", "--max-depth", "2")
rc.copy("src/", "remote:/dst", "--transfers", "8", "--exclude", "*.tmp")
Content helpers
text = rc.cat("remote:/notes.txt")
rc.rcat("remote:/notes.txt", "written from python") # reads from stdin
The escape hatch
Anything without a dedicated method is reachable via run() (returns an
RcloneResult) — arguments are still passed safely as a list:
result = rc.run("dedupe", "remote:/dir", "--dedupe-mode", "newest")
print(result.returncode, result.stdout)
Configuration
rc = Rclone(
binary="/usr/local/bin/rclone", # explicit path (else $RCLONE_BINARY, else PATH)
config="~/my-rclone.conf", # passed as --config
flags=["--fast-list"], # global flags applied to every command
debug=True, # log each command on the "rclone" logger
timeout=600, # seconds, for non-streaming commands
)
Error handling
from rclone import Rclone, RcloneCommandError
rc = Rclone()
try:
rc.copy("missing:", "remote:/dst")
except RcloneCommandError as exc:
print(exc.returncode) # e.g. 3
print(exc.stderr) # rclone's error output
# Or opt out of raising and inspect the result instead:
result = rc.check("a/", "b/") # check() does not raise on differences
if not result.success:
print("directories differ")
Migrating to 2.0
lsd()andlsl()now return alist[Entry](structured:name,size,mod_time,is_dir,count,raw) instead of alist[str]of raw rclone lines. Use[e.name for e in rc.lsd(path)]for just names, orls()which already returns names.
Migrating from 0.4.x
- Import is now
from rclone import Rclone(the oldfrom rclone.rclone import Rclonestill works). - The
unit=constructor argument is gone; the progress bar auto-scales units (B/KB/MB/GB). - Progress is opt-in per call via
progress=True(bar) orprogress=<callable>(callback) instead of being on by default. - Listing/size now return structured types (
list,SizeResult) and errors raiseRcloneCommandError. delete()works normally — the old artificial block is gone (the unsafe dynamic dispatch that motivated it was removed).execute("...")is still available; new code should preferrun(...).
License
MIT
Metadata
Release files for rclone 2.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| rclone-2.0.0.tar.gz | 13.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| rclone-2.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 26.2 kB
Release files / rclone-2.0.0.tar.gz
| Download URL | rclone-2.0.0.tar.gz |
|---|---|
| Size | 13.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
145698f95759af6efe915e72a8927c6f734cf9b4a5f5252c3771891b8f0098f5
|
|
BLAKE2b-256 checksum How to use checksums |
c44fe6d5dd6d518c5155ee0d75cf4e284f9e90204953ac32047a7f505c29b72c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.4.1 CPython/3.13.13 Darwin/25.5.0
|
Release files / rclone-2.0.0-py3-none-any.whl
| Download URL | rclone-2.0.0-py3-none-any.whl |
|---|---|
| Size | 12.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
031606b643789a61743dc9eb4ad5c686f668453e04683c76bc4ecf3af7bd46e9
|
|
BLAKE2b-256 checksum How to use checksums |
a32054cfd1fdd98b759e38ef5dd2ecb5dc3c6bc5ab5c0920ff3f58b0c81524e6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.4.1 CPython/3.13.13 Darwin/25.5.0
|