Add callback_pre_directory, callback_pre_file.
These callbacks lets you inspect the anticipated copy operation before it actually occurs. My particular motivation for adding this was to search for source files which have been renamed, so I can rename the matching destination file before it attempts to make a duplicate. With the addition of the BAIL sentinel, you could also use this to dynamically decide which dirs to skip, if the exclude_ sets are not good enough for you.
This commit is contained in:
parent
9ef8254191
commit
bab1c2bffc
1 changed files with 37 additions and 2 deletions
|
@ -8,6 +8,9 @@ import sys
|
||||||
from voussoirkit import bytestring
|
from voussoirkit import bytestring
|
||||||
from voussoirkit import pathclass
|
from voussoirkit import pathclass
|
||||||
from voussoirkit import ratelimiter
|
from voussoirkit import ratelimiter
|
||||||
|
from voussoirkit import sentinel
|
||||||
|
|
||||||
|
BAIL = sentinel.Sentinel('BAIL')
|
||||||
|
|
||||||
logging.basicConfig(level=logging.CRITICAL)
|
logging.basicConfig(level=logging.CRITICAL)
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
@ -92,6 +95,8 @@ def copy_dir(
|
||||||
callback_exclusion=None,
|
callback_exclusion=None,
|
||||||
callback_file_progress=None,
|
callback_file_progress=None,
|
||||||
callback_permission_denied=None,
|
callback_permission_denied=None,
|
||||||
|
callback_pre_directory=None,
|
||||||
|
callback_pre_file=None,
|
||||||
chunk_size=CHUNK_SIZE,
|
chunk_size=CHUNK_SIZE,
|
||||||
destination_new_root=None,
|
destination_new_root=None,
|
||||||
dry_run=False,
|
dry_run=False,
|
||||||
|
@ -121,7 +126,7 @@ def copy_dir(
|
||||||
The BYTE, KIBIBYTE, etc constants from module 'bytestring' may help.
|
The BYTE, KIBIBYTE, etc constants from module 'bytestring' may help.
|
||||||
|
|
||||||
callback_directory_progress:
|
callback_directory_progress:
|
||||||
This function will be called after each file copy with three parameters:
|
This function will be called after each file copy with three arguments:
|
||||||
name of file copied, number of bytes written to destination directory
|
name of file copied, number of bytes written to destination directory
|
||||||
so far, total bytes needed (based on precalcsize).
|
so far, total bytes needed (based on precalcsize).
|
||||||
If `precalcsize` is False, this function will receive written bytes
|
If `precalcsize` is False, this function will receive written bytes
|
||||||
|
@ -138,6 +143,19 @@ def copy_dir(
|
||||||
Will be passed into each individual `copy_file` operation as the
|
Will be passed into each individual `copy_file` operation as the
|
||||||
`callback_permission_denied` for that file.
|
`callback_permission_denied` for that file.
|
||||||
|
|
||||||
|
callback_pre_directory:
|
||||||
|
This function will be called before each directory and subdirectory
|
||||||
|
begins copying their files. It will be called with three arguments:
|
||||||
|
source directory, destination directory, dry_run.
|
||||||
|
This function may return the BAIL sentinel (return spinal.BAIL) and
|
||||||
|
that directory will be skipped.
|
||||||
|
Note: BAIL will only skip a single directory. If you wish to terminate
|
||||||
|
the entire copy procedure, use `stop_event` which will finish copying
|
||||||
|
the current file and then stop.
|
||||||
|
|
||||||
|
callback_pre_file:
|
||||||
|
Will be passed into each individual `copy_file` operation as the
|
||||||
|
`callback_pre_copy` for that file.
|
||||||
|
|
||||||
destination_new_root:
|
destination_new_root:
|
||||||
Determine the destination path by calling
|
Determine the destination path by calling
|
||||||
|
@ -213,6 +231,8 @@ def copy_dir(
|
||||||
total_bytes = 0
|
total_bytes = 0
|
||||||
|
|
||||||
callback_directory_progress = callback_directory_progress or do_nothing
|
callback_directory_progress = callback_directory_progress or do_nothing
|
||||||
|
callback_pre_directory = callback_pre_directory or do_nothing
|
||||||
|
callback_pre_file = callback_pre_file or do_nothing
|
||||||
bytes_per_second = limiter_or_none(bytes_per_second)
|
bytes_per_second = limiter_or_none(bytes_per_second)
|
||||||
files_per_second = limiter_or_none(files_per_second)
|
files_per_second = limiter_or_none(files_per_second)
|
||||||
|
|
||||||
|
@ -250,6 +270,8 @@ def copy_dir(
|
||||||
destination.absolute_path,
|
destination.absolute_path,
|
||||||
1
|
1
|
||||||
))
|
))
|
||||||
|
if callback_pre_directory(directory, destination_dir, dry_run=dry_run) is BAIL:
|
||||||
|
continue
|
||||||
destination_files = (destination_dir.with_child(file.basename) for file in files)
|
destination_files = (destination_dir.with_child(file.basename) for file in files)
|
||||||
for (source_file, destination_file) in zip(files, destination_files):
|
for (source_file, destination_file) in zip(files, destination_files):
|
||||||
yield (source_file, destination_file)
|
yield (source_file, destination_file)
|
||||||
|
@ -275,6 +297,7 @@ def copy_dir(
|
||||||
bytes_per_second=bytes_per_second,
|
bytes_per_second=bytes_per_second,
|
||||||
callback_progress=callback_file_progress,
|
callback_progress=callback_file_progress,
|
||||||
callback_permission_denied=callback_permission_denied,
|
callback_permission_denied=callback_permission_denied,
|
||||||
|
callback_pre_copy=callback_pre_file,
|
||||||
chunk_size=chunk_size,
|
chunk_size=chunk_size,
|
||||||
dry_run=dry_run,
|
dry_run=dry_run,
|
||||||
overwrite_old=overwrite_old,
|
overwrite_old=overwrite_old,
|
||||||
|
@ -302,6 +325,7 @@ def copy_file(
|
||||||
bytes_per_second=None,
|
bytes_per_second=None,
|
||||||
callback_progress=None,
|
callback_progress=None,
|
||||||
callback_permission_denied=None,
|
callback_permission_denied=None,
|
||||||
|
callback_pre_copy=None,
|
||||||
callback_validate_hash=None,
|
callback_validate_hash=None,
|
||||||
chunk_size=CHUNK_SIZE,
|
chunk_size=CHUNK_SIZE,
|
||||||
dry_run=False,
|
dry_run=False,
|
||||||
|
@ -335,6 +359,12 @@ def copy_file(
|
||||||
|
|
||||||
If not provided, the PermissionError is raised.
|
If not provided, the PermissionError is raised.
|
||||||
|
|
||||||
|
callback_pre_copy:
|
||||||
|
This function will be called just before the destination filepath is
|
||||||
|
created and the handles opened. It will be called with three arguments:
|
||||||
|
source file, destination file, dry_run.
|
||||||
|
This function may return the BAIL sentinel (return spinal.BAIL) and
|
||||||
|
that file will not be copied.
|
||||||
|
|
||||||
callback_progress:
|
callback_progress:
|
||||||
If provided, this function will be called after writing
|
If provided, this function will be called after writing
|
||||||
|
@ -376,6 +406,7 @@ def copy_file(
|
||||||
destination = pathclass.Path(destination)
|
destination = pathclass.Path(destination)
|
||||||
|
|
||||||
callback_progress = callback_progress or do_nothing
|
callback_progress = callback_progress or do_nothing
|
||||||
|
callback_pre_copy = callback_pre_copy or do_nothing
|
||||||
|
|
||||||
if destination.is_dir:
|
if destination.is_dir:
|
||||||
destination = destination.with_child(source.basename)
|
destination = destination.with_child(source.basename)
|
||||||
|
@ -400,7 +431,11 @@ def copy_file(
|
||||||
|
|
||||||
source_bytes = source.size
|
source_bytes = source.size
|
||||||
|
|
||||||
|
if callback_pre_copy(source, destination, dry_run=dry_run) is BAIL:
|
||||||
|
return [destination, 0]
|
||||||
|
|
||||||
os.makedirs(destination.parent.absolute_path, exist_ok=True)
|
os.makedirs(destination.parent.absolute_path, exist_ok=True)
|
||||||
|
|
||||||
def handlehelper(path, mode):
|
def handlehelper(path, mode):
|
||||||
try:
|
try:
|
||||||
handle = open(path.absolute_path, mode)
|
handle = open(path.absolute_path, mode)
|
||||||
|
@ -474,7 +509,7 @@ def copy_file(
|
||||||
|
|
||||||
return [destination, written_bytes]
|
return [destination, written_bytes]
|
||||||
|
|
||||||
def do_nothing(*args):
|
def do_nothing(*args, **kwargs):
|
||||||
'''
|
'''
|
||||||
Used by other functions as the default callback.
|
Used by other functions as the default callback.
|
||||||
'''
|
'''
|
||||||
|
|
Loading…
Reference in a new issue