WebDAV client library providing easy access to cloud storage services like Yandex.Drive, Dropbox, Google Drive, Box, and 4shared.
Advanced directory synchronization capabilities for keeping local and remote directories in sync. These operations provide intelligent synchronization with support for push, pull, and bidirectional synchronization, handling file comparisons and conflict resolution.
Uploads missing or changed files from local directory to remote directory, ensuring remote has all local changes.
def push(self, remote_directory: str, local_directory: str) -> None:
"""
Upload missing or changed files from local to remote directory.
Compares local and remote directories, then uploads files that are:
- Present locally but missing remotely
- Modified locally (newer modification time)
- Different in size between local and remote
Parameters:
- remote_directory: str, path to remote directory
- local_directory: str, path to local directory
Raises:
- LocalResourceNotFound: if local directory doesn't exist
- RemoteParentNotFound: if remote parent directory doesn't exist
- NotConnection: if connection to server fails
- NotEnoughSpace: if insufficient space on server
"""Downloads missing or changed files from remote directory to local directory, ensuring local has all remote changes.
def pull(self, remote_directory: str, local_directory: str) -> None:
"""
Download missing or changed files from remote to local directory.
Compares remote and local directories, then downloads files that are:
- Present remotely but missing locally
- Modified remotely (newer modification time)
- Different in size between remote and local
Parameters:
- remote_directory: str, path to remote directory
- local_directory: str, path to local directory
Raises:
- RemoteResourceNotFound: if remote directory doesn't exist
- LocalResourceNotFound: if local parent directory doesn't exist
- NotConnection: if connection to server fails
"""Synchronizes both directories bidirectionally, ensuring both local and remote directories have the latest version of all files.
def sync(self, remote_directory: str, local_directory: str) -> None:
"""
Bidirectionally synchronize local and remote directories.
Performs both push and pull operations to ensure both directories
contain the most recent version of all files. Handles conflicts by
preferring the file with the most recent modification time.
Parameters:
- remote_directory: str, path to remote directory
- local_directory: str, path to local directory
Raises:
- LocalResourceNotFound: if local directory doesn't exist
- RemoteResourceNotFound: if remote directory doesn't exist
- NotConnection: if connection to server fails
- NotEnoughSpace: if insufficient space on server
"""import webdav.client as wc
client = wc.Client({
'webdav_hostname': "https://webdav.server.com",
'webdav_login': "username",
'webdav_password': "password"
})
# Push local changes to remote
client.push("projects/website/", "~/Development/website/")
print("Local changes pushed to remote directory")# Pull remote changes to local
client.pull("documents/shared/", "~/Documents/shared/")
print("Remote changes pulled to local directory")# Synchronize both directions
client.sync("backup/important/", "~/Documents/important/")
print("Directories synchronized bidirectionally")from webdav.client import WebDavException, NotConnection, LocalResourceNotFound, RemoteResourceNotFound
def safe_sync(client, remote_dir, local_dir, operation="sync"):
try:
if operation == "push":
client.push(remote_dir, local_dir)
print(f"Successfully pushed {local_dir} to {remote_dir}")
elif operation == "pull":
client.pull(remote_dir, local_dir)
print(f"Successfully pulled {remote_dir} to {local_dir}")
elif operation == "sync":
client.sync(remote_dir, local_dir)
print(f"Successfully synchronized {remote_dir} and {local_dir}")
except LocalResourceNotFound as e:
print(f"Local directory not found: {e}")
except RemoteResourceNotFound as e:
print(f"Remote directory not found: {e}")
except NotConnection as e:
print(f"Connection failed: {e}")
except WebDavException as e:
print(f"WebDAV error: {e}")
# Use safe sync operations
safe_sync(client, "projects/webapp/", "~/Development/webapp/", "push")
safe_sync(client, "documents/reports/", "~/Documents/reports/", "pull")
safe_sync(client, "shared/collaboration/", "~/Shared/collaboration/", "sync")import time
import schedule
def sync_directories():
"""Periodic sync function"""
try:
client.sync("backup/documents/", "~/Documents/")
print(f"Sync completed at {time.strftime('%Y-%m-%d %H:%M:%S')}")
except Exception as e:
print(f"Sync failed: {e}")
# Schedule sync every hour
schedule.every().hour.do(sync_directories)
# Or run once daily at 2 AM
schedule.every().day.at("02:00").do(sync_directories)
# Keep scheduler running
while True:
schedule.run_pending()
time.sleep(60)import os
from datetime import datetime
def backup_project(project_name, local_path):
"""Backup project to WebDAV with timestamp"""
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
remote_backup_path = f"backups/{project_name}_{timestamp}/"
try:
# Create timestamped backup
client.push(remote_backup_path, local_path)
print(f"Project {project_name} backed up to {remote_backup_path}")
# Also maintain current backup
current_backup_path = f"backups/{project_name}_current/"
client.sync(current_backup_path, local_path)
print(f"Current backup updated at {current_backup_path}")
except Exception as e:
print(f"Backup failed for {project_name}: {e}")
# Backup multiple projects
projects = {
"webapp": "~/Development/webapp/",
"mobile_app": "~/Development/mobile/",
"documentation": "~/Documents/project_docs/"
}
for project, path in projects.items():
if os.path.exists(os.path.expanduser(path)):
backup_project(project, path)
else:
print(f"Local path not found: {path}")def collaborate_workflow(shared_remote_dir, local_work_dir):
"""Workflow for collaborative projects"""
print("Starting collaborative workflow...")
# 1. Pull latest changes from team
print("Pulling latest changes...")
client.pull(shared_remote_dir, local_work_dir)
# 2. Do local work here
print("Work on your changes locally...")
input("Press Enter when you've completed your local changes...")
# 3. Pull again to get any new changes before pushing
print("Pulling latest changes before push...")
client.pull(shared_remote_dir, local_work_dir)
# 4. Push your changes
print("Pushing your changes...")
client.push(shared_remote_dir, local_work_dir)
print("Collaborative workflow completed!")
# Use collaborative workflow
collaborate_workflow("team/project_alpha/", "~/Work/project_alpha/")import os
import fnmatch
def selective_sync(remote_dir, local_dir, include_patterns=None, exclude_patterns=None):
"""
Sync with file filtering based on patterns
Note: This is a conceptual example - actual implementation would need
to be built on top of the basic sync operations
"""
include_patterns = include_patterns or ['*']
exclude_patterns = exclude_patterns or []
print(f"Syncing {local_dir} with {remote_dir}")
print(f"Including: {include_patterns}")
print(f"Excluding: {exclude_patterns}")
# Basic sync (in real implementation, you'd filter before sync)
client.sync(remote_dir, local_dir)
# Post-sync cleanup of excluded files (conceptual)
if exclude_patterns:
print("Note: Full filtering would require custom implementation")
# Example: Sync only Python files, exclude cache
selective_sync(
"projects/python_app/",
"~/Development/python_app/",
include_patterns=['*.py', '*.txt', '*.md'],
exclude_patterns=['__pycache__/*', '*.pyc', '.git/*']
)import os
from datetime import datetime
class SyncMonitor:
def __init__(self, client):
self.client = client
self.sync_log = []
def log_sync(self, operation, remote_dir, local_dir, success, error=None):
"""Log sync operation result"""
log_entry = {
'timestamp': datetime.now().isoformat(),
'operation': operation,
'remote_dir': remote_dir,
'local_dir': local_dir,
'success': success,
'error': str(error) if error else None
}
self.sync_log.append(log_entry)
def monitored_sync(self, remote_dir, local_dir, operation="sync"):
"""Perform sync with monitoring"""
try:
print(f"Starting {operation} operation...")
if operation == "push":
self.client.push(remote_dir, local_dir)
elif operation == "pull":
self.client.pull(remote_dir, local_dir)
else:
self.client.sync(remote_dir, local_dir)
self.log_sync(operation, remote_dir, local_dir, True)
print(f"{operation.capitalize()} completed successfully")
except Exception as e:
self.log_sync(operation, remote_dir, local_dir, False, e)
print(f"{operation.capitalize()} failed: {e}")
def get_sync_report(self):
"""Generate sync status report"""
total_ops = len(self.sync_log)
successful_ops = sum(1 for log in self.sync_log if log['success'])
print(f"\nSync Report:")
print(f"Total operations: {total_ops}")
print(f"Successful: {successful_ops}")
print(f"Failed: {total_ops - successful_ops}")
# Show recent failures
recent_failures = [log for log in self.sync_log[-10:] if not log['success']]
if recent_failures:
print("\nRecent failures:")
for failure in recent_failures:
print(f" {failure['timestamp']}: {failure['operation']} - {failure['error']}")
# Use sync monitor
monitor = SyncMonitor(client)
monitor.monitored_sync("projects/website/", "~/Development/website/", "sync")
monitor.monitored_sync("documents/", "~/Documents/", "pull")
monitor.get_sync_report()Install with Tessl CLI
npx tessl i tessl/pypi-webdavclient