Skip to content

DirectoryObject

Directory Core API Reference

API Documentation

hands_scaphoid.objects.DirectoryObject

Directory operations module for hands-scaphoid package.

This module provides the Directory class for pure directory operations without context management.

File

name: DirectoryOperations.py uuid: dd9a984d-8031-4c2d-bb99-92dc0c7797f0 date: 2025-09-16

Description

Pure directory operations class - no context management

Authors: ["Andreas Häberle"]

Classes

DirectoryObject

Bases: ObjectItem

Pure directory operations class without context management.

This class provides static methods for directory operations that can be used independently of any context manager. All methods operate on explicit directory paths and do not maintain any state.

Example
Direct directory operations

Directory.create_directory(Path("myproject")) files = Directory.list_contents(Path("myproject")) Directory.copy_directory(Path("source"), Path("target"))

Source code in src/hands_scaphoid/objects/DirectoryObject.py
class DirectoryObject(ObjectItem):
    """
    Pure directory operations class without context management.

    This class provides static methods for directory operations that can be used
    independently of any context manager. All methods operate on explicit
    directory paths and do not maintain any state.

    Example:
        # Direct directory operations
        Directory.create_directory(Path("myproject"))
        files = Directory.list_contents(Path("myproject"))
        Directory.copy_directory(Path("source"), Path("target"))
    """

    def __init__(self, name: str, path: str):
        super().__init__(name, path)

    def __repr__(self):
        return f"DirectoryObject(name={self.name}, path={self.value})"

    @staticmethod
    def create_directory(
        dir_path: PathLike, parents: bool = True, exist_ok: bool = True
    ) -> None:
        """
        Create a directory.

        Args:
            dir_path: Path to the directory to create
            parents: Whether to create parent directories if they don't exist
            exist_ok: Whether to raise an error if directory already exists

        Raises:
            PermissionError: If lacking create permissions
            FileExistsError: If directory exists and exist_ok is False
        """
        path = Path(dir_path)
        try:
            path.mkdir(parents=parents, exist_ok=exist_ok)
            console.print(f"[green]Created directory:[/green] {path}")
        except FileExistsError:
            if not exist_ok:
                console.print(f"[red]Directory already exists:[/red] {path}")
                raise
        except PermissionError:
            console.print(f"[red]Permission denied creating directory:[/red] {path}")
            raise
        except Exception as e:
            console.print(f"[red]Error creating directory {path}:[/red] {e}")
            raise

    @staticmethod
    def list_contents(dir_path: PathLike) -> list[str]:
        """
        List all items in a directory.

        Args:
            dir_path: Path to the directory to list

        Returns:
            List of file and directory names

        Raises:
            FileNotFoundError: If directory doesn't exist
            PermissionError: If lacking read permissions
        """
        path = Path(dir_path)
        try:
            if not path.exists():
                raise FileNotFoundError(f"Directory not found: {path}")
            if not path.is_dir():
                raise NotADirectoryError(f"Path is not a directory: {path}")

            contents = [item.name for item in path.iterdir()]
            console.print(f"[blue]Listed {len(contents)} items in:[/blue] {path}")
            return sorted(contents)
        except PermissionError:
            console.print(f"[red]Permission denied reading directory:[/red] {path}")
            raise
        except Exception as e:
            console.print(f"[red]Error listing directory {path}:[/red] {e}")
            raise

    @staticmethod
    def list_files(dir_path: PathLike, extension: str | None = None) -> list[str]:
        """
        List files in a directory, optionally filtered by extension.

        Args:
            dir_path: Path to the directory to list
            extension: File extension filter (without dot, e.g., 'txt')

        Returns:
            List of file names
        """
        path = Path(dir_path)
        try:
            if not path.exists():
                raise FileNotFoundError(f"Directory not found: {path}")
            if not path.is_dir():
                raise NotADirectoryError(f"Path is not a directory: {path}")

            files = []
            for item in path.iterdir():
                if item.is_file():
                    if (
                        extension is None
                        or item.suffix.lstrip(".").lower() == extension.lower()
                    ):
                        files.append(item.name)

            console.print(f"[blue]Found {len(files)} files in:[/blue] {path}")
            return sorted(files)
        except PermissionError:
            console.print(f"[red]Permission denied reading directory:[/red] {path}")
            raise
        except Exception as e:
            console.print(f"[red]Error listing files in {path}:[/red] {e}")
            raise

    @staticmethod
    def list_directories(dir_path: PathLike) -> list[str]:
        """
        List subdirectories in a directory.

        Args:
            dir_path: Path to the directory to list

        Returns:
            List of subdirectory names
        """
        path = Path(dir_path)
        try:
            if not path.exists():
                raise FileNotFoundError(f"Directory not found: {path}")
            if not path.is_dir():
                raise NotADirectoryError(f"Path is not a directory: {path}")

            dirs = []
            for item in path.iterdir():
                if item.is_dir():
                    dirs.append(item.name)

            console.print(f"[blue]Found {len(dirs)} subdirectories in:[/blue] {path}")
            return sorted(dirs)
        except PermissionError:
            console.print(f"[red]Permission denied reading directory:[/red] {path}")
            raise
        except Exception as e:
            console.print(f"[red]Error listing directories in {path}:[/red] {e}")
            raise

    @staticmethod
    def directory_exists(dir_path: PathLike) -> bool:
        """
        Check if a directory exists.

        Args:
            dir_path: Path to check

        Returns:
            True if directory exists and is a directory, False otherwise
        """
        path = Path(dir_path)
        return path.exists() and path.is_dir()

    @staticmethod
    def is_empty(dir_path: PathLike) -> bool:
        """
        Check if a directory is empty.

        Args:
            dir_path: Path to the directory to check

        Returns:
            True if directory is empty, False otherwise

        Raises:
            FileNotFoundError: If directory doesn't exist
            NotADirectoryError: If path is not a directory
        """
        path = Path(dir_path)
        if not path.exists():
            raise FileNotFoundError(f"Directory not found: {path}")
        if not path.is_dir():
            raise NotADirectoryError(f"Path is not a directory: {path}")

        return len(list(path.iterdir())) == 0

    @staticmethod
    def copy_directory(
        source_path: PathLike, target_path: PathLike, dirs_exist_ok: bool = False
    ) -> None:
        """
        Copy a directory and its contents to a new location.

        Args:
            source_path: Path to the source directory
            target_path: Path to the target directory
            dirs_exist_ok: Whether to allow copying to existing directory

        Raises:
            FileNotFoundError: If source directory doesn't exist
            PermissionError: If lacking copy permissions
        """
        source = Path(source_path)
        target = Path(target_path)

        if not source.exists():
            raise FileNotFoundError(f"Source directory not found: {source}")
        if not source.is_dir():
            raise NotADirectoryError(f"Source path is not a directory: {source}")

        try:
            shutil.copytree(source, target, dirs_exist_ok=dirs_exist_ok)
            console.print(f"[green]Copied directory:[/green] {source}{target}")
        except PermissionError:
            console.print(
                f"[red]Permission denied copying directory:[/red] {source}{target}"
            )
            raise
        except Exception as e:
            console.print(
                f"[red]Error copying directory {source}{target}:[/red] {e}"
            )
            raise

    @staticmethod
    def move_directory(source_path: PathLike, target_path: PathLike) -> None:
        """
        Move a directory to a new location.

        Args:
            source_path: Path to the source directory
            target_path: Path to the target directory

        Raises:
            FileNotFoundError: If source directory doesn't exist
            PermissionError: If lacking move permissions
        """
        source = Path(source_path)
        target = Path(target_path)

        if not source.exists():
            raise FileNotFoundError(f"Source directory not found: {source}")
        if not source.is_dir():
            raise NotADirectoryError(f"Source path is not a directory: {source}")

        try:
            shutil.move(str(source), str(target))
            console.print(f"[green]Moved directory:[/green] {source}{target}")
        except PermissionError:
            console.print(
                f"[red]Permission denied moving directory:[/red] {source}{target}"
            )
            raise
        except Exception as e:
            console.print(f"[red]Error moving directory {source}{target}:[/red] {e}")
            raise

    @staticmethod
    def delete_directory(dir_path: PathLike, recursive: bool = False) -> None:
        """
        Delete a directory.

        Args:
            dir_path: Path to the directory to delete
            recursive: Whether to delete directory and all its contents

        Raises:
            FileNotFoundError: If directory doesn't exist
            PermissionError: If lacking delete permissions
            OSError: If directory is not empty and recursive is False
        """
        path = Path(dir_path)

        if not path.exists():
            raise FileNotFoundError(f"Directory not found: {path}")
        if not path.is_dir():
            raise NotADirectoryError(f"Path is not a directory: {path}")

        try:
            if recursive:
                shutil.rmtree(path)
                console.print(f"[green]Deleted directory recursively:[/green] {path}")
            else:
                path.rmdir()
                console.print(f"[green]Deleted directory:[/green] {path}")
        except PermissionError:
            console.print(f"[red]Permission denied deleting directory:[/red] {path}")
            raise
        except OSError as e:
            if "not empty" in str(e).lower():
                console.print(
                    f"[red]Directory not empty (use recursive=True):[/red] {path}"
                )
            else:
                console.print(f"[red]Error deleting directory {path}:[/red] {e}")
            raise
        except Exception as e:
            console.print(f"[red]Error deleting directory {path}:[/red] {e}")
            raise

    @staticmethod
    def get_directory_size(dir_path: PathLike) -> int:
        """
        Get the total size of a directory and its contents in bytes.

        Args:
            dir_path: Path to the directory

        Returns:
            Total size in bytes

        Raises:
            FileNotFoundError: If directory doesn't exist
        """
        path = Path(dir_path)
        if not path.exists():
            raise FileNotFoundError(f"Directory not found: {path}")
        if not path.is_dir():
            raise NotADirectoryError(f"Path is not a directory: {path}")

        total_size = 0
        try:
            for item in path.rglob("*"):
                if item.is_file():
                    total_size += item.stat().st_size
            return total_size
        except PermissionError:
            console.print(
                f"[red]Permission denied accessing some files in:[/red] {path}"
            )
            raise
        except Exception as e:
            console.print(f"[red]Error calculating directory size {path}:[/red] {e}")
            raise

    @staticmethod
    def change_directory(dir_path: PathLike) -> str:
        """
        Change the current working directory.

        Args:
            dir_path: Path to change to

        Returns:
            Previous working directory path

        Raises:
            FileNotFoundError: If directory doesn't exist
            PermissionError: If lacking access permissions
        """
        path = Path(dir_path)

        if not path.exists():
            raise FileNotFoundError(f"Directory not found: {path}")
        if not path.is_dir():
            raise NotADirectoryError(f"Path is not a directory: {path}")

        try:
            previous_cwd = os.getcwd()
            os.chdir(path)
            console.print(f"[blue]Changed directory to:[/blue] {path}")
            return previous_cwd
        except PermissionError:
            console.print(f"[red]Permission denied changing to directory:[/red] {path}")
            raise
        except Exception as e:
            console.print(f"[red]Error changing to directory {path}:[/red] {e}")
            raise
Functions
create_directory(dir_path, parents=True, exist_ok=True) staticmethod

Create a directory.

Parameters:

Name Type Description Default
dir_path PathLike

Path to the directory to create

required
parents bool

Whether to create parent directories if they don't exist

True
exist_ok bool

Whether to raise an error if directory already exists

True

Raises:

Type Description
PermissionError

If lacking create permissions

FileExistsError

If directory exists and exist_ok is False

Source code in src/hands_scaphoid/objects/DirectoryObject.py
@staticmethod
def create_directory(
    dir_path: PathLike, parents: bool = True, exist_ok: bool = True
) -> None:
    """
    Create a directory.

    Args:
        dir_path: Path to the directory to create
        parents: Whether to create parent directories if they don't exist
        exist_ok: Whether to raise an error if directory already exists

    Raises:
        PermissionError: If lacking create permissions
        FileExistsError: If directory exists and exist_ok is False
    """
    path = Path(dir_path)
    try:
        path.mkdir(parents=parents, exist_ok=exist_ok)
        console.print(f"[green]Created directory:[/green] {path}")
    except FileExistsError:
        if not exist_ok:
            console.print(f"[red]Directory already exists:[/red] {path}")
            raise
    except PermissionError:
        console.print(f"[red]Permission denied creating directory:[/red] {path}")
        raise
    except Exception as e:
        console.print(f"[red]Error creating directory {path}:[/red] {e}")
        raise
list_contents(dir_path) staticmethod

List all items in a directory.

Parameters:

Name Type Description Default
dir_path PathLike

Path to the directory to list

required

Returns:

Type Description
list[str]

List of file and directory names

Raises:

Type Description
FileNotFoundError

If directory doesn't exist

PermissionError

If lacking read permissions

Source code in src/hands_scaphoid/objects/DirectoryObject.py
@staticmethod
def list_contents(dir_path: PathLike) -> list[str]:
    """
    List all items in a directory.

    Args:
        dir_path: Path to the directory to list

    Returns:
        List of file and directory names

    Raises:
        FileNotFoundError: If directory doesn't exist
        PermissionError: If lacking read permissions
    """
    path = Path(dir_path)
    try:
        if not path.exists():
            raise FileNotFoundError(f"Directory not found: {path}")
        if not path.is_dir():
            raise NotADirectoryError(f"Path is not a directory: {path}")

        contents = [item.name for item in path.iterdir()]
        console.print(f"[blue]Listed {len(contents)} items in:[/blue] {path}")
        return sorted(contents)
    except PermissionError:
        console.print(f"[red]Permission denied reading directory:[/red] {path}")
        raise
    except Exception as e:
        console.print(f"[red]Error listing directory {path}:[/red] {e}")
        raise
list_files(dir_path, extension=None) staticmethod

List files in a directory, optionally filtered by extension.

Parameters:

Name Type Description Default
dir_path PathLike

Path to the directory to list

required
extension str | None

File extension filter (without dot, e.g., 'txt')

None

Returns:

Type Description
list[str]

List of file names

Source code in src/hands_scaphoid/objects/DirectoryObject.py
@staticmethod
def list_files(dir_path: PathLike, extension: str | None = None) -> list[str]:
    """
    List files in a directory, optionally filtered by extension.

    Args:
        dir_path: Path to the directory to list
        extension: File extension filter (without dot, e.g., 'txt')

    Returns:
        List of file names
    """
    path = Path(dir_path)
    try:
        if not path.exists():
            raise FileNotFoundError(f"Directory not found: {path}")
        if not path.is_dir():
            raise NotADirectoryError(f"Path is not a directory: {path}")

        files = []
        for item in path.iterdir():
            if item.is_file():
                if (
                    extension is None
                    or item.suffix.lstrip(".").lower() == extension.lower()
                ):
                    files.append(item.name)

        console.print(f"[blue]Found {len(files)} files in:[/blue] {path}")
        return sorted(files)
    except PermissionError:
        console.print(f"[red]Permission denied reading directory:[/red] {path}")
        raise
    except Exception as e:
        console.print(f"[red]Error listing files in {path}:[/red] {e}")
        raise
list_directories(dir_path) staticmethod

List subdirectories in a directory.

Parameters:

Name Type Description Default
dir_path PathLike

Path to the directory to list

required

Returns:

Type Description
list[str]

List of subdirectory names

Source code in src/hands_scaphoid/objects/DirectoryObject.py
@staticmethod
def list_directories(dir_path: PathLike) -> list[str]:
    """
    List subdirectories in a directory.

    Args:
        dir_path: Path to the directory to list

    Returns:
        List of subdirectory names
    """
    path = Path(dir_path)
    try:
        if not path.exists():
            raise FileNotFoundError(f"Directory not found: {path}")
        if not path.is_dir():
            raise NotADirectoryError(f"Path is not a directory: {path}")

        dirs = []
        for item in path.iterdir():
            if item.is_dir():
                dirs.append(item.name)

        console.print(f"[blue]Found {len(dirs)} subdirectories in:[/blue] {path}")
        return sorted(dirs)
    except PermissionError:
        console.print(f"[red]Permission denied reading directory:[/red] {path}")
        raise
    except Exception as e:
        console.print(f"[red]Error listing directories in {path}:[/red] {e}")
        raise
directory_exists(dir_path) staticmethod

Check if a directory exists.

Parameters:

Name Type Description Default
dir_path PathLike

Path to check

required

Returns:

Type Description
bool

True if directory exists and is a directory, False otherwise

Source code in src/hands_scaphoid/objects/DirectoryObject.py
@staticmethod
def directory_exists(dir_path: PathLike) -> bool:
    """
    Check if a directory exists.

    Args:
        dir_path: Path to check

    Returns:
        True if directory exists and is a directory, False otherwise
    """
    path = Path(dir_path)
    return path.exists() and path.is_dir()
is_empty(dir_path) staticmethod

Check if a directory is empty.

Parameters:

Name Type Description Default
dir_path PathLike

Path to the directory to check

required

Returns:

Type Description
bool

True if directory is empty, False otherwise

Raises:

Type Description
FileNotFoundError

If directory doesn't exist

NotADirectoryError

If path is not a directory

Source code in src/hands_scaphoid/objects/DirectoryObject.py
@staticmethod
def is_empty(dir_path: PathLike) -> bool:
    """
    Check if a directory is empty.

    Args:
        dir_path: Path to the directory to check

    Returns:
        True if directory is empty, False otherwise

    Raises:
        FileNotFoundError: If directory doesn't exist
        NotADirectoryError: If path is not a directory
    """
    path = Path(dir_path)
    if not path.exists():
        raise FileNotFoundError(f"Directory not found: {path}")
    if not path.is_dir():
        raise NotADirectoryError(f"Path is not a directory: {path}")

    return len(list(path.iterdir())) == 0
copy_directory(source_path, target_path, dirs_exist_ok=False) staticmethod

Copy a directory and its contents to a new location.

Parameters:

Name Type Description Default
source_path PathLike

Path to the source directory

required
target_path PathLike

Path to the target directory

required
dirs_exist_ok bool

Whether to allow copying to existing directory

False

Raises:

Type Description
FileNotFoundError

If source directory doesn't exist

PermissionError

If lacking copy permissions

Source code in src/hands_scaphoid/objects/DirectoryObject.py
@staticmethod
def copy_directory(
    source_path: PathLike, target_path: PathLike, dirs_exist_ok: bool = False
) -> None:
    """
    Copy a directory and its contents to a new location.

    Args:
        source_path: Path to the source directory
        target_path: Path to the target directory
        dirs_exist_ok: Whether to allow copying to existing directory

    Raises:
        FileNotFoundError: If source directory doesn't exist
        PermissionError: If lacking copy permissions
    """
    source = Path(source_path)
    target = Path(target_path)

    if not source.exists():
        raise FileNotFoundError(f"Source directory not found: {source}")
    if not source.is_dir():
        raise NotADirectoryError(f"Source path is not a directory: {source}")

    try:
        shutil.copytree(source, target, dirs_exist_ok=dirs_exist_ok)
        console.print(f"[green]Copied directory:[/green] {source}{target}")
    except PermissionError:
        console.print(
            f"[red]Permission denied copying directory:[/red] {source}{target}"
        )
        raise
    except Exception as e:
        console.print(
            f"[red]Error copying directory {source}{target}:[/red] {e}"
        )
        raise
move_directory(source_path, target_path) staticmethod

Move a directory to a new location.

Parameters:

Name Type Description Default
source_path PathLike

Path to the source directory

required
target_path PathLike

Path to the target directory

required

Raises:

Type Description
FileNotFoundError

If source directory doesn't exist

PermissionError

If lacking move permissions

Source code in src/hands_scaphoid/objects/DirectoryObject.py
@staticmethod
def move_directory(source_path: PathLike, target_path: PathLike) -> None:
    """
    Move a directory to a new location.

    Args:
        source_path: Path to the source directory
        target_path: Path to the target directory

    Raises:
        FileNotFoundError: If source directory doesn't exist
        PermissionError: If lacking move permissions
    """
    source = Path(source_path)
    target = Path(target_path)

    if not source.exists():
        raise FileNotFoundError(f"Source directory not found: {source}")
    if not source.is_dir():
        raise NotADirectoryError(f"Source path is not a directory: {source}")

    try:
        shutil.move(str(source), str(target))
        console.print(f"[green]Moved directory:[/green] {source}{target}")
    except PermissionError:
        console.print(
            f"[red]Permission denied moving directory:[/red] {source}{target}"
        )
        raise
    except Exception as e:
        console.print(f"[red]Error moving directory {source}{target}:[/red] {e}")
        raise
delete_directory(dir_path, recursive=False) staticmethod

Delete a directory.

Parameters:

Name Type Description Default
dir_path PathLike

Path to the directory to delete

required
recursive bool

Whether to delete directory and all its contents

False

Raises:

Type Description
FileNotFoundError

If directory doesn't exist

PermissionError

If lacking delete permissions

OSError

If directory is not empty and recursive is False

Source code in src/hands_scaphoid/objects/DirectoryObject.py
@staticmethod
def delete_directory(dir_path: PathLike, recursive: bool = False) -> None:
    """
    Delete a directory.

    Args:
        dir_path: Path to the directory to delete
        recursive: Whether to delete directory and all its contents

    Raises:
        FileNotFoundError: If directory doesn't exist
        PermissionError: If lacking delete permissions
        OSError: If directory is not empty and recursive is False
    """
    path = Path(dir_path)

    if not path.exists():
        raise FileNotFoundError(f"Directory not found: {path}")
    if not path.is_dir():
        raise NotADirectoryError(f"Path is not a directory: {path}")

    try:
        if recursive:
            shutil.rmtree(path)
            console.print(f"[green]Deleted directory recursively:[/green] {path}")
        else:
            path.rmdir()
            console.print(f"[green]Deleted directory:[/green] {path}")
    except PermissionError:
        console.print(f"[red]Permission denied deleting directory:[/red] {path}")
        raise
    except OSError as e:
        if "not empty" in str(e).lower():
            console.print(
                f"[red]Directory not empty (use recursive=True):[/red] {path}"
            )
        else:
            console.print(f"[red]Error deleting directory {path}:[/red] {e}")
        raise
    except Exception as e:
        console.print(f"[red]Error deleting directory {path}:[/red] {e}")
        raise
get_directory_size(dir_path) staticmethod

Get the total size of a directory and its contents in bytes.

Parameters:

Name Type Description Default
dir_path PathLike

Path to the directory

required

Returns:

Type Description
int

Total size in bytes

Raises:

Type Description
FileNotFoundError

If directory doesn't exist

Source code in src/hands_scaphoid/objects/DirectoryObject.py
@staticmethod
def get_directory_size(dir_path: PathLike) -> int:
    """
    Get the total size of a directory and its contents in bytes.

    Args:
        dir_path: Path to the directory

    Returns:
        Total size in bytes

    Raises:
        FileNotFoundError: If directory doesn't exist
    """
    path = Path(dir_path)
    if not path.exists():
        raise FileNotFoundError(f"Directory not found: {path}")
    if not path.is_dir():
        raise NotADirectoryError(f"Path is not a directory: {path}")

    total_size = 0
    try:
        for item in path.rglob("*"):
            if item.is_file():
                total_size += item.stat().st_size
        return total_size
    except PermissionError:
        console.print(
            f"[red]Permission denied accessing some files in:[/red] {path}"
        )
        raise
    except Exception as e:
        console.print(f"[red]Error calculating directory size {path}:[/red] {e}")
        raise
change_directory(dir_path) staticmethod

Change the current working directory.

Parameters:

Name Type Description Default
dir_path PathLike

Path to change to

required

Returns:

Type Description
str

Previous working directory path

Raises:

Type Description
FileNotFoundError

If directory doesn't exist

PermissionError

If lacking access permissions

Source code in src/hands_scaphoid/objects/DirectoryObject.py
@staticmethod
def change_directory(dir_path: PathLike) -> str:
    """
    Change the current working directory.

    Args:
        dir_path: Path to change to

    Returns:
        Previous working directory path

    Raises:
        FileNotFoundError: If directory doesn't exist
        PermissionError: If lacking access permissions
    """
    path = Path(dir_path)

    if not path.exists():
        raise FileNotFoundError(f"Directory not found: {path}")
    if not path.is_dir():
        raise NotADirectoryError(f"Path is not a directory: {path}")

    try:
        previous_cwd = os.getcwd()
        os.chdir(path)
        console.print(f"[blue]Changed directory to:[/blue] {path}")
        return previous_cwd
    except PermissionError:
        console.print(f"[red]Permission denied changing to directory:[/red] {path}")
        raise
    except Exception as e:
        console.print(f"[red]Error changing to directory {path}:[/red] {e}")
        raise