@@ -258,6 +258,56 @@ def copydir(fs1, fs2, create_destination=True, ignore_errors=False, chunk_size=6
258258 chunk_size = chunk_size )
259259
260260
261+ def copydir_progress (progress_callback , fs1 , fs2 , create_destination = True , ignore_errors = False , chunk_size = 64 * 1024 ):
262+ """
263+ Copies the contents of a directory from one fs to another, with a callback function to display progress.
264+
265+ `progress_callback` should be a function with two parameters; `step` and `num_steps`.
266+
267+ `num_steps` is the number of steps in the copy process, and `step` is the current step. `num_steps` may be None if the number
268+ of steps is still being calculated.
269+
270+ """
271+ if isinstance (fs1 , tuple ):
272+ fs1 , dir1 = fs1
273+ fs1 = fs1 .opendir (dir1 )
274+ if isinstance (fs2 , tuple ):
275+ fs2 , dir2 = fs2
276+ if create_destination :
277+ fs2 .makedir (dir2 , allow_recreate = True , recursive = True )
278+ fs2 = fs2 .opendir (dir2 )
279+
280+ def do_callback (step , num_steps ):
281+ try :
282+ progress_callback (step , num_steps )
283+ except :
284+ pass
285+
286+ do_callback (0 , None )
287+
288+ file_count = 0
289+ copy_paths = []
290+ for dir_path , file_paths in fs1 .walk ():
291+ copy_paths .append ((dir_path , file_paths ))
292+ file_count += len (file_paths )
293+ do_callback (0 , file_count )
294+
295+ step = 0
296+ for i , (dir_path , file_paths ) in enumerate (copy_paths ):
297+ try :
298+ fs2 .makedir (dir_path , allow_recreate = True )
299+ for path in file_paths :
300+ copy_path = pathjoin (dir_path , path )
301+ with fs1 .open (copy_path , 'rb' ) as src_file :
302+ fs2 .setcontents (copy_path , src_file , chunk_size = chunk_size )
303+ step += 1
304+ except :
305+ if ignore_errors :
306+ continue
307+ raise
308+ do_callback (step , file_count )
309+
310+
261311def remove_all (fs , path ):
262312 """Remove everything in a directory. Returns True if successful.
263313
0 commit comments