0
# Synchronization
1
2
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.
3
4
## Capabilities
5
6
### Push Synchronization
7
8
Uploads missing or changed files from local directory to remote directory, ensuring remote has all local changes.
9
10
```python { .api }
11
def push(self, remote_directory: str, local_directory: str) -> None:
12
"""
13
Upload missing or changed files from local to remote directory.
14
15
Compares local and remote directories, then uploads files that are:
16
- Present locally but missing remotely
17
- Modified locally (newer modification time)
18
- Different in size between local and remote
19
20
Parameters:
21
- remote_directory: str, path to remote directory
22
- local_directory: str, path to local directory
23
24
Raises:
25
- LocalResourceNotFound: if local directory doesn't exist
26
- RemoteParentNotFound: if remote parent directory doesn't exist
27
- NotConnection: if connection to server fails
28
- NotEnoughSpace: if insufficient space on server
29
"""
30
```
31
32
### Pull Synchronization
33
34
Downloads missing or changed files from remote directory to local directory, ensuring local has all remote changes.
35
36
```python { .api }
37
def pull(self, remote_directory: str, local_directory: str) -> None:
38
"""
39
Download missing or changed files from remote to local directory.
40
41
Compares remote and local directories, then downloads files that are:
42
- Present remotely but missing locally
43
- Modified remotely (newer modification time)
44
- Different in size between remote and local
45
46
Parameters:
47
- remote_directory: str, path to remote directory
48
- local_directory: str, path to local directory
49
50
Raises:
51
- RemoteResourceNotFound: if remote directory doesn't exist
52
- LocalResourceNotFound: if local parent directory doesn't exist
53
- NotConnection: if connection to server fails
54
"""
55
```
56
57
### Bidirectional Synchronization
58
59
Synchronizes both directories bidirectionally, ensuring both local and remote directories have the latest version of all files.
60
61
```python { .api }
62
def sync(self, remote_directory: str, local_directory: str) -> None:
63
"""
64
Bidirectionally synchronize local and remote directories.
65
66
Performs both push and pull operations to ensure both directories
67
contain the most recent version of all files. Handles conflicts by
68
preferring the file with the most recent modification time.
69
70
Parameters:
71
- remote_directory: str, path to remote directory
72
- local_directory: str, path to local directory
73
74
Raises:
75
- LocalResourceNotFound: if local directory doesn't exist
76
- RemoteResourceNotFound: if remote directory doesn't exist
77
- NotConnection: if connection to server fails
78
- NotEnoughSpace: if insufficient space on server
79
"""
80
```
81
82
## Usage Examples
83
84
### Basic Push Operation
85
86
```python
87
import webdav.client as wc
88
89
client = wc.Client({
90
'webdav_hostname': "https://webdav.server.com",
91
'webdav_login': "username",
92
'webdav_password': "password"
93
})
94
95
# Push local changes to remote
96
client.push("projects/website/", "~/Development/website/")
97
print("Local changes pushed to remote directory")
98
```
99
100
### Basic Pull Operation
101
102
```python
103
# Pull remote changes to local
104
client.pull("documents/shared/", "~/Documents/shared/")
105
print("Remote changes pulled to local directory")
106
```
107
108
### Full Bidirectional Sync
109
110
```python
111
# Synchronize both directions
112
client.sync("backup/important/", "~/Documents/important/")
113
print("Directories synchronized bidirectionally")
114
```
115
116
### Sync with Error Handling
117
118
```python
119
from webdav.client import WebDavException, NotConnection, LocalResourceNotFound, RemoteResourceNotFound
120
121
def safe_sync(client, remote_dir, local_dir, operation="sync"):
122
try:
123
if operation == "push":
124
client.push(remote_dir, local_dir)
125
print(f"Successfully pushed {local_dir} to {remote_dir}")
126
elif operation == "pull":
127
client.pull(remote_dir, local_dir)
128
print(f"Successfully pulled {remote_dir} to {local_dir}")
129
elif operation == "sync":
130
client.sync(remote_dir, local_dir)
131
print(f"Successfully synchronized {remote_dir} and {local_dir}")
132
133
except LocalResourceNotFound as e:
134
print(f"Local directory not found: {e}")
135
except RemoteResourceNotFound as e:
136
print(f"Remote directory not found: {e}")
137
except NotConnection as e:
138
print(f"Connection failed: {e}")
139
except WebDavException as e:
140
print(f"WebDAV error: {e}")
141
142
# Use safe sync operations
143
safe_sync(client, "projects/webapp/", "~/Development/webapp/", "push")
144
safe_sync(client, "documents/reports/", "~/Documents/reports/", "pull")
145
safe_sync(client, "shared/collaboration/", "~/Shared/collaboration/", "sync")
146
```
147
148
### Periodic Synchronization
149
150
```python
151
import time
152
import schedule
153
154
def sync_directories():
155
"""Periodic sync function"""
156
try:
157
client.sync("backup/documents/", "~/Documents/")
158
print(f"Sync completed at {time.strftime('%Y-%m-%d %H:%M:%S')}")
159
except Exception as e:
160
print(f"Sync failed: {e}")
161
162
# Schedule sync every hour
163
schedule.every().hour.do(sync_directories)
164
165
# Or run once daily at 2 AM
166
schedule.every().day.at("02:00").do(sync_directories)
167
168
# Keep scheduler running
169
while True:
170
schedule.run_pending()
171
time.sleep(60)
172
```
173
174
### Project Backup Sync
175
176
```python
177
import os
178
from datetime import datetime
179
180
def backup_project(project_name, local_path):
181
"""Backup project to WebDAV with timestamp"""
182
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
183
remote_backup_path = f"backups/{project_name}_{timestamp}/"
184
185
try:
186
# Create timestamped backup
187
client.push(remote_backup_path, local_path)
188
print(f"Project {project_name} backed up to {remote_backup_path}")
189
190
# Also maintain current backup
191
current_backup_path = f"backups/{project_name}_current/"
192
client.sync(current_backup_path, local_path)
193
print(f"Current backup updated at {current_backup_path}")
194
195
except Exception as e:
196
print(f"Backup failed for {project_name}: {e}")
197
198
# Backup multiple projects
199
projects = {
200
"webapp": "~/Development/webapp/",
201
"mobile_app": "~/Development/mobile/",
202
"documentation": "~/Documents/project_docs/"
203
}
204
205
for project, path in projects.items():
206
if os.path.exists(os.path.expanduser(path)):
207
backup_project(project, path)
208
else:
209
print(f"Local path not found: {path}")
210
```
211
212
### Collaborative Workflow
213
214
```python
215
def collaborate_workflow(shared_remote_dir, local_work_dir):
216
"""Workflow for collaborative projects"""
217
218
print("Starting collaborative workflow...")
219
220
# 1. Pull latest changes from team
221
print("Pulling latest changes...")
222
client.pull(shared_remote_dir, local_work_dir)
223
224
# 2. Do local work here
225
print("Work on your changes locally...")
226
input("Press Enter when you've completed your local changes...")
227
228
# 3. Pull again to get any new changes before pushing
229
print("Pulling latest changes before push...")
230
client.pull(shared_remote_dir, local_work_dir)
231
232
# 4. Push your changes
233
print("Pushing your changes...")
234
client.push(shared_remote_dir, local_work_dir)
235
236
print("Collaborative workflow completed!")
237
238
# Use collaborative workflow
239
collaborate_workflow("team/project_alpha/", "~/Work/project_alpha/")
240
```
241
242
### Selective Synchronization
243
244
```python
245
import os
246
import fnmatch
247
248
def selective_sync(remote_dir, local_dir, include_patterns=None, exclude_patterns=None):
249
"""
250
Sync with file filtering based on patterns
251
Note: This is a conceptual example - actual implementation would need
252
to be built on top of the basic sync operations
253
"""
254
255
include_patterns = include_patterns or ['*']
256
exclude_patterns = exclude_patterns or []
257
258
print(f"Syncing {local_dir} with {remote_dir}")
259
print(f"Including: {include_patterns}")
260
print(f"Excluding: {exclude_patterns}")
261
262
# Basic sync (in real implementation, you'd filter before sync)
263
client.sync(remote_dir, local_dir)
264
265
# Post-sync cleanup of excluded files (conceptual)
266
if exclude_patterns:
267
print("Note: Full filtering would require custom implementation")
268
269
# Example: Sync only Python files, exclude cache
270
selective_sync(
271
"projects/python_app/",
272
"~/Development/python_app/",
273
include_patterns=['*.py', '*.txt', '*.md'],
274
exclude_patterns=['__pycache__/*', '*.pyc', '.git/*']
275
)
276
```
277
278
### Sync Status Monitoring
279
280
```python
281
import os
282
from datetime import datetime
283
284
class SyncMonitor:
285
def __init__(self, client):
286
self.client = client
287
self.sync_log = []
288
289
def log_sync(self, operation, remote_dir, local_dir, success, error=None):
290
"""Log sync operation result"""
291
log_entry = {
292
'timestamp': datetime.now().isoformat(),
293
'operation': operation,
294
'remote_dir': remote_dir,
295
'local_dir': local_dir,
296
'success': success,
297
'error': str(error) if error else None
298
}
299
self.sync_log.append(log_entry)
300
301
def monitored_sync(self, remote_dir, local_dir, operation="sync"):
302
"""Perform sync with monitoring"""
303
try:
304
print(f"Starting {operation} operation...")
305
306
if operation == "push":
307
self.client.push(remote_dir, local_dir)
308
elif operation == "pull":
309
self.client.pull(remote_dir, local_dir)
310
else:
311
self.client.sync(remote_dir, local_dir)
312
313
self.log_sync(operation, remote_dir, local_dir, True)
314
print(f"{operation.capitalize()} completed successfully")
315
316
except Exception as e:
317
self.log_sync(operation, remote_dir, local_dir, False, e)
318
print(f"{operation.capitalize()} failed: {e}")
319
320
def get_sync_report(self):
321
"""Generate sync status report"""
322
total_ops = len(self.sync_log)
323
successful_ops = sum(1 for log in self.sync_log if log['success'])
324
325
print(f"\nSync Report:")
326
print(f"Total operations: {total_ops}")
327
print(f"Successful: {successful_ops}")
328
print(f"Failed: {total_ops - successful_ops}")
329
330
# Show recent failures
331
recent_failures = [log for log in self.sync_log[-10:] if not log['success']]
332
if recent_failures:
333
print("\nRecent failures:")
334
for failure in recent_failures:
335
print(f" {failure['timestamp']}: {failure['operation']} - {failure['error']}")
336
337
# Use sync monitor
338
monitor = SyncMonitor(client)
339
monitor.monitored_sync("projects/website/", "~/Development/website/", "sync")
340
monitor.monitored_sync("documents/", "~/Documents/", "pull")
341
monitor.get_sync_report()
342
```