Move

Move files or entire folder structures from source to destination.

Move


Processing

Moves files or entire folder structures from a source location to a destination.

Inputs

source
Path or list of paths to the files or directories to move.
destination
Path to the destination directory where files or directories will be moved.

Inputs Types

Input Types
source Str, Path, List
destination Str, DirectoryPath

You can check the list of supported types here: Available Type Hints.

Outputs

moved items
List of paths to files or directories successfully moved.
skipped items
List of paths to files or directories skipped during the move operation (e.g., due to overwrite settings).
errors
List of error messages for any files or directories that could not be moved.

Outputs Types

Output Types
moved items List
skipped items List
errors List

You can check the list of supported types here: Available Type Hints.

Options

The Move brick contains some changeable options:

Overwrite
If enabled, overwrites existing files or directories at the destination. Defaults to False.
Verbose
If enabled, logs detailed messages about the move operation, including errors. 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 _move_single_file(
    src_file, dst_file, result, overwrite, verbose, brick_display_name
):
    """Move 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.move(src_file, dst_file)
        result["moved_items"].append(str(dst_file))
        verbose and logger.info(
            f"[{brick_display_name}] '{src_file}' was moved to '{dst_file}'."
        )
    except OSError as e:
        if e.errno == errno.EROFS:
            verbose and logger.error(
                f"[{brick_display_name}]  Failed to move file '{src_file}': File system is read-only."
            )
            result["errors"].append(f"Failed to move file '{src_file}': {e}")
        else:
            verbose and logger.error(
                f"[{brick_display_name}]  Failed to move file '{src_file}'."
            )
            result["errors"].append(f"Failed to move file '{src_file}': {e}")
    except PermissionError as e:
        verbose and logger.error(
            f"[{brick_display_name}] Failed to move file '{src_file}': Permission denied."
        )
        result["errors"].append(f"Failed to move file '{src_file}': {e}")
    except Exception as e:
        verbose and logger.error(
            f"[{brick_display_name}] Failed to move file '{src_file}'."
        )
        result["errors"].append(f"Failed to move file '{src_file}': {e}")


def _move_directory(src_dir, dst_dir, result, overwrite, verbose, brick_display_name):
    """Move 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.move(src_dir, dst_dir)
        result["moved_items"].append(str(dst_dir))
        verbose and logger.info(
            f"[{brick_display_name}] '{src_dir}' was moved to '{dst_dir}'."
        )
    except OSError as e:
        if e.errno == errno.EROFS:
            verbose and logger.error(
                f"[{brick_display_name}]  Failed to move directory '{src_dir}': File system is read-only."
            )
            result["errors"].append(f"Failed to move directory '{src_dir}': {e}")
        else:
            verbose and logger.error(
                f"[{brick_display_name}]  Failed to move directory '{src_dir}'."
            )
            result["errors"].append(f"Failed to move directory '{src_dir}': {e}")
    except PermissionError as e:
        verbose and logger.error(
            f"[{brick_display_name}] Failed to move directory '{src_dir}': Permission denied."
        )
        result["errors"].append(f"Failed to move directory '{src_dir}': {e}")
    except Exception as e:
        verbose and logger.error(
            f"[{brick_display_name}] Failed to move directory '{src_dir}'."
        )
        result["errors"].append(f"Failed to move directory '{src_dir}': {e}")


def move_files(
    source: Union[Str, Path, List], destination: Union[Str, DirectoryPath], options=None
) -> Tuple[List, List, List]:
    brick_display_name = "Move"
    options = options or {}
    verbose = options.get("verbose", True)
    overwrite = options.get("overwrite", True)
    result = {"moved_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():
            _move_single_file(
                src_path, dst_path, result, overwrite, verbose, brick_display_name
            )
        elif src_path.is_dir():
            _move_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}'.")
    moved_items = result["moved_items"]
    skipped_items = result["skipped_items"]
    errors = result["errors"]
    return (moved_items, skipped_items, errors)

Brick Info

version v0.1.4
python 3.10, 3.11, 3.12, 3.13
requirements
    -