sysrsync: Python wrapper for rsync
sysrsync is a simple and safe Python wrapper designed to execute the system's `rsync` command-line utility. It provides a programmatic interface to construct `rsync` commands, helping to prevent common shell injection vulnerabilities and simplify complex `rsync` operations. The current version is 1.1.1, and it maintains an active but infrequent release cadence, primarily for bug fixes and minor enhancements.
Common errors
-
sysrsync.exceptions.SysrsyncError: rsync command failed with exit code 127
cause The `rsync` command-line utility was not found in the system's PATH.fixInstall `rsync` on your operating system (e.g., `sudo apt-get install rsync` or `brew install rsync`) and verify it's accessible from your shell. -
sysrsync.exceptions.SysrsyncError: rsync command failed with exit code 255
cause A generic SSH error, often related to authentication failure, hostname resolution, or network connectivity when syncing to a remote destination.fixCheck your SSH configuration, ensure the `destination_ssh_user` has proper SSH keys or passwordless access, verify `destination_ssh_host` is reachable, and confirm firewall rules. Inspect the `stderr` output from the exception for more specific SSH messages. -
sysrsync.exceptions.SysrsyncError: rsync command failed with exit code 23
cause Partial transfer due to an error. This often indicates issues with permissions, disk space, or a file/directory not found on the source or destination.fixExamine the `stderr` output from the `SysrsyncError` exception. It will usually contain specific `rsync` messages indicating which file failed to transfer and why (e.g., 'Permission denied', 'No such file or directory', 'No space left on device'). Adjust permissions, free up space, or correct paths accordingly.
Warnings
- gotcha Prior to version 1.1.1, `sysrsync` had a bug that could lead to false strict host checking errors or unexpected SSH connection failures when using remote destinations. This often required manual intervention or workarounds.
- gotcha The `sysrsync` library is a wrapper around the system's `rsync` command. If `rsync` is not installed or not in your system's PATH, `sysrsync` calls will fail with an OS-level error indicating the command cannot be found.
- breaking In version 0.3.0, the functionality that checks if the remote source exists was disabled. This means `sysrsync` no longer performs an implicit check for remote source existence before initiating the `rsync` transfer.
- gotcha Understanding `rsync`'s trailing slash behavior is critical. A trailing slash on the source path (`source="folder/"`) copies the *contents* of the folder, while no trailing slash (`source="folder"`) copies the folder itself into the destination. Misunderstanding this is a common source of unexpected sync results.
Install
-
pip install sysrsync
Imports
- run
from sysrsync import SysRsync
import sysrsync result = sysrsync.run(...)
Quickstart
import sysrsync
import os
# Create dummy files for demonstration
if not os.path.exists("my_source_folder"): os.makedirs("my_source_folder")
with open("my_source_folder/file1.txt", "w") as f: f.write("Hello")
with open("my_source_folder/file2.txt", "w") as f: f.write("World")
# Example: Local to Local sync (replace with your actual paths)
# This requires rsync to be installed on your system.
# In a real scenario, 'destination_ssh_user' and 'destination_ssh_host' would be used for remote sync.
print("Attempting local sync...")
try:
result = sysrsync.run(
source="my_source_folder/", # Trailing slash means copy contents of folder
destination="./my_destination_folder/",
options=["-a", "--delete", "-v"],
sync=True # Wait for completion
)
print(f"Rsync completed with exit code: {result.returncode}")
print(f"STDOUT:\n{result.stdout.decode()}")
if result.stderr: print(f"STDERR:\n{result.stderr.decode()}")
except sysrsync.exceptions.SysrsyncError as e:
print(f"Rsync failed: {e}")
print(f"STDOUT:\n{e.result.stdout.decode()}")
if e.result.stderr: print(f"STDERR:\n{e.result.stderr.decode()}")
# Clean up dummy files
import shutil
if os.path.exists("my_source_folder"): shutil.rmtree("my_source_folder")
if os.path.exists("my_destination_folder"): shutil.rmtree("my_destination_folder")