Skip to content

File Utilities

renderkit.io.file_utils.FileUtils

Utility class for file operations.

Source code in src/renderkit/io/file_utils.py
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
class FileUtils:
    """Utility class for file operations."""

    @staticmethod
    def ensure_directory(path: Path) -> None:
        """Ensure a directory exists, creating it if necessary.

        Args:
            path: Path to the directory
        """
        path.mkdir(parents=True, exist_ok=True)

    @staticmethod
    def get_file_extension(path: Path) -> str:
        """Get the file extension (lowercase, without dot).

        Args:
            path: Path to the file

        Returns:
            File extension
        """
        return path.suffix.lower().lstrip(".")

    @staticmethod
    def is_image_file(path: Path) -> bool:
        """Check if a file is a supported image format.

        Args:
            path: Path to the file

        Returns:
            True if file is a supported image format
        """
        return FileUtils.get_file_extension(path) in constants.OIIO_SUPPORTED_EXTENSIONS

    @staticmethod
    def find_files_by_pattern(directory: Path, pattern: str, recursive: bool = False) -> list[Path]:
        """Find files matching a pattern.

        Args:
            directory: Directory to search
            pattern: Filename pattern (supports wildcards)
            recursive: Whether to search recursively

        Returns:
            List of matching file paths
        """
        if not directory.exists():
            logger.warning(f"Directory does not exist: {directory}")
            return []

        if recursive:
            return list(directory.rglob(pattern))
        return list(directory.glob(pattern))

    @staticmethod
    def get_file_size(path: Path) -> int:
        """Get file size in bytes.

        Args:
            path: Path to the file

        Returns:
            File size in bytes, or 0 if file doesn't exist
        """
        if path.exists():
            return path.stat().st_size
        return 0

    @staticmethod
    def validate_output_path(path: Path, overwrite: bool = False) -> bool:
        """Validate that output path can be written to.

        Args:
            path: Output file path
            overwrite: Whether to allow overwriting existing files

        Returns:
            True if path is valid for writing
        """
        if path.exists() and not overwrite:
            logger.warning(f"Output file already exists: {path}")
            return False

        # Ensure parent directory exists
        FileUtils.ensure_directory(path.parent)
        return True

    @staticmethod
    def validate_output_filename(path_str: str) -> tuple[bool, str]:
        """Check if the output filename is valid for video encoding.

        Returns:
            Tuple of (is_valid, error_message)
        """
        if not path_str.strip():
            return False, "Output path is empty."

        path = Path(path_str.strip())

        # Check extension
        ext = path.suffix.lower().lstrip(".")
        if not ext:
            return False, "Output path must have a file extension (e.g., .mp4)."

        if ext not in constants.SUPPORTED_VIDEO_EXTENSIONS:
            return (
                False,
                f"Unsupported video extension: .{ext}. Supported: {', '.join(constants.SUPPORTED_VIDEO_EXTENSIONS)}",
            )

        # Check for invalid characters in filename (basic check)
        invalid_chars = '<>:"|?*'
        if any(c in path.name for c in invalid_chars):
            return False, f"Filename contains invalid characters: {invalid_chars}"

        return True, ""

    @staticmethod
    def convert_path_to_pattern(path_str: str) -> str:
        """Convert a file path to a frame sequence pattern string."""
        path = Path(path_str)
        filename = path.name
        matches = list(re.finditer(r"\d+", filename))
        if not matches:
            return str(path)

        last_match = matches[-1]
        padding = len(last_match.group(0))
        token = "#" * padding
        pattern_name = f"{filename[: last_match.start()]}{token}{filename[last_match.end() :]}"
        return str(path.with_name(pattern_name))

    @staticmethod
    def detect_sequence(pattern: str) -> list[int]:
        """Detect a frame sequence and return the frame numbers."""
        sequence = SequenceDetector.detect_sequence(pattern)
        return sequence.frame_numbers

    @staticmethod
    def get_sample_frame_from_pattern(pattern: str) -> Optional[Path]:
        """Get a sample frame path from a sequence pattern."""
        try:
            sequence = SequenceDetector.detect_sequence(pattern)
        except Exception as exc:
            logger.debug(f"Could not detect sequence for preview: {exc}")
            return None

        if not sequence.frame_numbers:
            return None

        return sequence.get_file_path(sequence.frame_numbers[0])

convert_path_to_pattern(path_str) staticmethod

Convert a file path to a frame sequence pattern string.

Source code in src/renderkit/io/file_utils.py
133
134
135
136
137
138
139
140
141
142
143
144
145
146
@staticmethod
def convert_path_to_pattern(path_str: str) -> str:
    """Convert a file path to a frame sequence pattern string."""
    path = Path(path_str)
    filename = path.name
    matches = list(re.finditer(r"\d+", filename))
    if not matches:
        return str(path)

    last_match = matches[-1]
    padding = len(last_match.group(0))
    token = "#" * padding
    pattern_name = f"{filename[: last_match.start()]}{token}{filename[last_match.end() :]}"
    return str(path.with_name(pattern_name))

detect_sequence(pattern) staticmethod

Detect a frame sequence and return the frame numbers.

Source code in src/renderkit/io/file_utils.py
148
149
150
151
152
@staticmethod
def detect_sequence(pattern: str) -> list[int]:
    """Detect a frame sequence and return the frame numbers."""
    sequence = SequenceDetector.detect_sequence(pattern)
    return sequence.frame_numbers

ensure_directory(path) staticmethod

Ensure a directory exists, creating it if necessary.

Parameters:

Name Type Description Default
path Path

Path to the directory

required
Source code in src/renderkit/io/file_utils.py
17
18
19
20
21
22
23
24
@staticmethod
def ensure_directory(path: Path) -> None:
    """Ensure a directory exists, creating it if necessary.

    Args:
        path: Path to the directory
    """
    path.mkdir(parents=True, exist_ok=True)

find_files_by_pattern(directory, pattern, recursive=False) staticmethod

Find files matching a pattern.

Parameters:

Name Type Description Default
directory Path

Directory to search

required
pattern str

Filename pattern (supports wildcards)

required
recursive bool

Whether to search recursively

False

Returns:

Type Description
list[Path]

List of matching file paths

Source code in src/renderkit/io/file_utils.py
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
@staticmethod
def find_files_by_pattern(directory: Path, pattern: str, recursive: bool = False) -> list[Path]:
    """Find files matching a pattern.

    Args:
        directory: Directory to search
        pattern: Filename pattern (supports wildcards)
        recursive: Whether to search recursively

    Returns:
        List of matching file paths
    """
    if not directory.exists():
        logger.warning(f"Directory does not exist: {directory}")
        return []

    if recursive:
        return list(directory.rglob(pattern))
    return list(directory.glob(pattern))

get_file_extension(path) staticmethod

Get the file extension (lowercase, without dot).

Parameters:

Name Type Description Default
path Path

Path to the file

required

Returns:

Type Description
str

File extension

Source code in src/renderkit/io/file_utils.py
26
27
28
29
30
31
32
33
34
35
36
@staticmethod
def get_file_extension(path: Path) -> str:
    """Get the file extension (lowercase, without dot).

    Args:
        path: Path to the file

    Returns:
        File extension
    """
    return path.suffix.lower().lstrip(".")

get_file_size(path) staticmethod

Get file size in bytes.

Parameters:

Name Type Description Default
path Path

Path to the file

required

Returns:

Type Description
int

File size in bytes, or 0 if file doesn't exist

Source code in src/renderkit/io/file_utils.py
70
71
72
73
74
75
76
77
78
79
80
81
82
@staticmethod
def get_file_size(path: Path) -> int:
    """Get file size in bytes.

    Args:
        path: Path to the file

    Returns:
        File size in bytes, or 0 if file doesn't exist
    """
    if path.exists():
        return path.stat().st_size
    return 0

get_sample_frame_from_pattern(pattern) staticmethod

Get a sample frame path from a sequence pattern.

Source code in src/renderkit/io/file_utils.py
154
155
156
157
158
159
160
161
162
163
164
165
166
@staticmethod
def get_sample_frame_from_pattern(pattern: str) -> Optional[Path]:
    """Get a sample frame path from a sequence pattern."""
    try:
        sequence = SequenceDetector.detect_sequence(pattern)
    except Exception as exc:
        logger.debug(f"Could not detect sequence for preview: {exc}")
        return None

    if not sequence.frame_numbers:
        return None

    return sequence.get_file_path(sequence.frame_numbers[0])

is_image_file(path) staticmethod

Check if a file is a supported image format.

Parameters:

Name Type Description Default
path Path

Path to the file

required

Returns:

Type Description
bool

True if file is a supported image format

Source code in src/renderkit/io/file_utils.py
38
39
40
41
42
43
44
45
46
47
48
@staticmethod
def is_image_file(path: Path) -> bool:
    """Check if a file is a supported image format.

    Args:
        path: Path to the file

    Returns:
        True if file is a supported image format
    """
    return FileUtils.get_file_extension(path) in constants.OIIO_SUPPORTED_EXTENSIONS

validate_output_filename(path_str) staticmethod

Check if the output filename is valid for video encoding.

Returns:

Type Description
tuple[bool, str]

Tuple of (is_valid, error_message)

Source code in src/renderkit/io/file_utils.py
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
@staticmethod
def validate_output_filename(path_str: str) -> tuple[bool, str]:
    """Check if the output filename is valid for video encoding.

    Returns:
        Tuple of (is_valid, error_message)
    """
    if not path_str.strip():
        return False, "Output path is empty."

    path = Path(path_str.strip())

    # Check extension
    ext = path.suffix.lower().lstrip(".")
    if not ext:
        return False, "Output path must have a file extension (e.g., .mp4)."

    if ext not in constants.SUPPORTED_VIDEO_EXTENSIONS:
        return (
            False,
            f"Unsupported video extension: .{ext}. Supported: {', '.join(constants.SUPPORTED_VIDEO_EXTENSIONS)}",
        )

    # Check for invalid characters in filename (basic check)
    invalid_chars = '<>:"|?*'
    if any(c in path.name for c in invalid_chars):
        return False, f"Filename contains invalid characters: {invalid_chars}"

    return True, ""

validate_output_path(path, overwrite=False) staticmethod

Validate that output path can be written to.

Parameters:

Name Type Description Default
path Path

Output file path

required
overwrite bool

Whether to allow overwriting existing files

False

Returns:

Type Description
bool

True if path is valid for writing

Source code in src/renderkit/io/file_utils.py
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
@staticmethod
def validate_output_path(path: Path, overwrite: bool = False) -> bool:
    """Validate that output path can be written to.

    Args:
        path: Output file path
        overwrite: Whether to allow overwriting existing files

    Returns:
        True if path is valid for writing
    """
    if path.exists() and not overwrite:
        logger.warning(f"Output file already exists: {path}")
        return False

    # Ensure parent directory exists
    FileUtils.ensure_directory(path.parent)
    return True