Copy
Copy files or entire folder structures from source to destination.
Copy
Processing
This function copies files or entire folder structures from a specified source to a destination directory. It handles single files, directories, or lists of paths and provides the list of copied, skipped, or errored items.
Inputs
- source
- The file(s) or folder(s) to copy. Can be a single path or a list of paths.
- destination
- The target directory where files or folders will be copied.
Inputs Types
Input | Types |
---|---|
source |
Str , Path , List |
destination |
Str , DirectoryPath |
You can check the list of supported types here: Available Type Hints.
Outputs
- copied items
- A list of paths successfully copied to the destination.
- skipped items
- A list of paths that were not copied (e.g., due to overwrite settings).
- errors
- A list of error messages for paths that failed to copy.
Outputs Types
Output | Types |
---|---|
copied items |
List |
skipped items |
List |
errors |
List |
You can check the list of supported types here: Available Type Hints.
Options
The Copy brick contains some changeable options:
- Overwrite
- If enabled, overwrites existing files or folders in the destination. Defaults to False.
- Verbose
- If enabled, logs detailed information about the copy process, including errors and skipped items. Defaults to True.
import logging
import errno
import shutil
from coded_flows.types import Str, Path, DirectoryPath, Tuple, Union, List
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def _validate_source_input(src):
"""Validate that source input contains only strings and Path objects."""
if isinstance(src, (str, Path)):
return True
elif isinstance(src, list):
return all((isinstance(item, (str, Path)) for item in src))
return False
def _copy_single_file(
src_file, dst_file, result, overwrite, verbose, brick_display_name
):
"""Copy a single file and return True if successful."""
try:
if dst_file.exists() and (not overwrite):
result["skipped_items"].append(str(src_file))
verbose and logger.warning(
f"[{brick_display_name}] '{src_file}' was skipped because it already exists."
)
return
dst_file.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(src_file, dst_file)
result["copied_items"].append(str(dst_file))
verbose and logger.info(
f"[{brick_display_name}] The '{dst_file}' copy was created."
)
except OSError as e:
if e.errno == errno.EROFS:
verbose and logger.error(
f"[{brick_display_name}] Failed to copy file '{src_file}': File system is read-only."
)
result["errors"].append(f"Failed to copy file '{src_file}': {e}")
else:
verbose and logger.error(
f"[{brick_display_name}] Failed to copy file '{src_file}'."
)
result["errors"].append(f"Failed to copy file '{src_file}': {e}")
except PermissionError as e:
verbose and logger.error(
f"[{brick_display_name}] Failed to copy file '{src_file}': Permission denied."
)
result["errors"].append(f"Failed to copy file '{src_file}': {e}")
except Exception as e:
verbose and logger.error(
f"[{brick_display_name}] Failed to copy file '{src_file}'."
)
result["errors"].append(f"Failed to copy file '{src_file}': {e}")
def _copy_directory(src_dir, dst_dir, result, overwrite, verbose, brick_display_name):
"""Copy entire directory structure."""
try:
if dst_dir.exists() and (not overwrite):
result["skipped_items"].append(str(src_dir))
verbose and logger.warning(
f"[{brick_display_name}] '{src_dir}' was skipped because it already exists."
)
return
if dst_dir.exists() and overwrite:
shutil.rmtree(dst_dir)
shutil.copytree(src_dir, dst_dir)
result["copied_items"].append(str(dst_dir))
verbose and logger.info(
f"[{brick_display_name}] The '{dst_dir}' copy was created."
)
except OSError as e:
if e.errno == errno.EROFS:
verbose and logger.error(
f"[{brick_display_name}] Failed to copy file '{src_dir}': File system is read-only."
)
result["errors"].append(f"Failed to copy file '{src_dir}': {e}")
else:
verbose and logger.error(
f"[{brick_display_name}] Failed to copy file '{src_dir}'."
)
result["errors"].append(f"Failed to copy file '{src_dir}': {e}")
except PermissionError as e:
verbose and logger.error(
f"[{brick_display_name}] Failed to copy file '{src_dir}': Permission denied."
)
result["errors"].append(f"Failed to copy file '{src_dir}': {e}")
except Exception as e:
verbose and logger.error(
f"[{brick_display_name}] Failed to copy file '{src_dir}'."
)
result["errors"].append(f"Failed to copy file '{src_dir}': {e}")
def copy_files(
source: Union[Str, Path, List], destination: Union[Str, DirectoryPath], options=None
) -> Tuple[List, List, List]:
brick_display_name = "Copy"
options = options or {}
verbose = options.get("verbose", True)
overwrite = options.get("overwrite", True)
result = {"copied_items": [], "skipped_items": [], "errors": []}
if not _validate_source_input(source):
verbose and logger.error(
f"[{brick_display_name}] Invalid source input. Must be Str, Path, or list of Str/Path objects."
)
raise ValueError(
"Invalid source input. Must be Str, Path, or list of Str/Path objects."
)
if isinstance(source, (str, Path)):
source_paths = [Path(source)]
else:
source_paths = source
destination_path = Path(destination)
try:
destination_path.mkdir(parents=True, exist_ok=True)
except PermissionError as e:
verbose and logger.error(
f"[{brick_display_name}] Failed to create destination directory '{destination_path}': Permission denied."
)
raise PermissionError(
f"Failed to create destination directory '{destination_path}': Permission denied."
)
except Exception as e:
verbose and logger.error(
f"[{brick_display_name}] Failed to create destination directory."
)
raise OSError(f"Failed to create destination directory: {e}")
for source_item in source_paths:
src_path = Path(source_item)
if not src_path.exists():
result["errors"].append(f"Source path does not exist: '{source_item}'.")
verbose and logger.error(
f"[{brick_display_name}] Source path does not exist: '{source_item}'."
)
continue
dst_path = destination_path / src_path.name
if src_path.is_file():
_copy_single_file(
src_path, dst_path, result, overwrite, verbose, brick_display_name
)
elif src_path.is_dir():
_copy_directory(
src_path, dst_path, result, overwrite, verbose, brick_display_name
)
else:
verbose and logger.error(
f"[{brick_display_name}] Unknown file type: '{source_item}'."
)
result["errors"].append(f"Unknown file type: '{source_item}'.")
copied_items = result["copied_items"]
skipped_items = result["skipped_items"]
errors = result["errors"]
return (copied_items, skipped_items, errors)
Brick Info
version
v0.1.4
python
3.10,
3.11,
3.12,
3.13
requirements
-
-