Module

utils.file_io

File I/O utilities with robust error handling.

Provides standardized file reading/writing operations with consistent error handling, logging, and encoding fallback. Consolidates duplicate file I/O patterns found throughout the codebase.

Example:

from bengal.utils.file_io import read_text_file, load_json, load_yaml, rmtree_robust

# Read text file with encoding fallback
content = read_text_file(path, fallback_encoding='latin-1')

# Load JSON with error handling
data = load_json(path, on_error='return_empty')

# Auto-detect and load data file
data = load_data_file(path)  # Works for .json, .yaml, .toml

# Robust directory removal (handles macOS quirks)
rmtree_robust(Path('/path/to/dir'))

Functions

_strip_bom
Strip UTF-8 BOM from content if present.
4 str
def _strip_bom(content: str, file_path: Path, encoding: str, caller: str | None = None) -> str

Strip UTF-8 BOM from content if present.

Parameters 4

Name Type Default Description
content str

File content

file_path Path

Path to file (for logging)

encoding str

Encoding used (for logging)

caller str | None None

Caller identifier for logging

Returns

str

Content with BOM removed if present, otherwise unchanged

read_text_file
Read text file with robust error handling and encoding fallback. Consolidates patterns from: - ben…
5 str | None
def read_text_file(file_path: Path | str, encoding: str = 'utf-8', fallback_encoding: str | None = 'latin-1', on_error: str = 'raise', caller: str | None = None) -> str | None

Read text file with robust error handling and encoding fallback.

Consolidates patterns from:

  • bengal/discovery/content_discovery.py:192 (UTF-8 with latin-1 fallback)
  • bengal/rendering/template_functions/files.py:78 (file reading with logging)
  • bengal/config/loader.py:137 (config file reading)

Parameters 5

Name Type Default Description
file_path Path | str

Path to file to read

encoding str 'utf-8'

Primary encoding to try (default: 'utf-8')

fallback_encoding str | None 'latin-1'

Fallback encoding if primary fails (default: 'latin-1')

on_error str 'raise'

Error handling strategy: - 'raise': Raise exception on error - 'return_empty': Return empty string on error - 'return_none': Return None on error

caller str | None None

Caller identifier for logging context

Returns

str | None

File contents as string, or None/empty string based on on_error.

Encoding notes:

  • Strips UTF-8 BOM when present.
  • If primary decode fails, triesutf-8-sigbefore the configured fallback.

load_json
Load JSON file with error handling. Consolidates patterns from: - bengal/rendering/template_functi…
3 Any
def load_json(file_path: Path | str, on_error: str = 'return_empty', caller: str | None = None) -> Any

Load JSON file with error handling.

Consolidates patterns from:

  • bengal/rendering/template_functions/data.py:80 (JSON loading)

Parameters 3

Name Type Default Description
file_path Path | str

Path to JSON file

on_error str 'return_empty'

Error handling strategy ('raise', 'return_empty', 'return_none')

caller str | None None

Caller identifier for logging

Returns

Any

Parsed JSON data, or {} / None based on on_error

load_yaml
Load YAML file with error handling. Consolidates patterns from: - bengal/config/loader.py:142 (YAM…
3 dict[str, Any] | None
def load_yaml(file_path: Path | str, on_error: str = 'return_empty', caller: str | None = None) -> dict[str, Any] | None

Load YAML file with error handling.

Consolidates patterns from:

  • bengal/config/loader.py:142 (YAML config loading)
  • bengal/rendering/template_functions/data.py:94 (YAML data loading)

Parameters 3

Name Type Default Description
file_path Path | str

Path to YAML file

on_error str 'return_empty'

Error handling strategy ('raise', 'return_empty', 'return_none')

caller str | None None

Caller identifier for logging

Returns

dict[str, Any] | None

Parsed YAML data, or {} / None based on on_error

load_toml
Load TOML file with error handling. Consolidates patterns from: - bengal/config/loader.py:137 (TOM…
3 dict[str, Any] | None
def load_toml(file_path: Path | str, on_error: str = 'return_empty', caller: str | None = None) -> dict[str, Any] | None

Load TOML file with error handling.

Consolidates patterns from:

  • bengal/config/loader.py:137 (TOML config loading)

Parameters 3

Name Type Default Description
file_path Path | str

Path to TOML file

on_error str 'return_empty'

Error handling strategy ('raise', 'return_empty', 'return_none')

caller str | None None

Caller identifier for logging

Returns

dict[str, Any] | None

Parsed TOML data, or {} / None based on on_error

load_data_file
Auto-detect and load JSON/YAML/TOML file. Consolidates pattern from: - bengal/rendering/template_f…
3 dict[str, Any] | None
def load_data_file(file_path: Path | str, on_error: str = 'return_empty', caller: str | None = None) -> dict[str, Any] | None

Auto-detect and load JSON/YAML/TOML file.

Consolidates pattern from:

  • bengal/rendering/template_functions/data.py:40 (get_data function)

Parameters 3

Name Type Default Description
file_path Path | str

Path to data file (.json, .yaml, .yml, .toml)

on_error str 'return_empty'

Error handling strategy ('raise', 'return_empty', 'return_none')

caller str | None None

Caller identifier for logging

Returns

dict[str, Any] | None

Parsed data, or {} / None based on on_error

write_text_file
Write text to file with parent directory creation.
5 None
def write_text_file(file_path: Path | str, content: str, encoding: str = 'utf-8', create_parents: bool = True, caller: str | None = None) -> None

Write text to file with parent directory creation.

Parameters 5

Name Type Default Description
file_path Path | str

Path to file to write

content str

Text content to write

encoding str 'utf-8'

Text encoding (default: 'utf-8')

create_parents bool True

Create parent directories if they don't exist

caller str | None None

Caller identifier for logging

write_json
Write data as JSON file.
5 None
def write_json(file_path: Path | str, data: Any, indent: int | None = 2, create_parents: bool = True, caller: str | None = None) -> None

Write data as JSON file.

Parameters 5

Name Type Default Description
file_path Path | str

Path to JSON file

data Any

Data to serialize as JSON

indent int | None 2

JSON indentation (None for compact)

create_parents bool True

Create parent directories if needed

caller str | None None

Caller identifier for logging

_remove_hidden_files
Remove macOS hidden files that may prevent directory deletion. Targets .DS_Store, ._* files, and o…
1 int
def _remove_hidden_files(dir_path: Path) -> int

Remove macOS hidden files that may prevent directory deletion.

Targets .DS_Store, ._* files, and other dotfiles that macOS creates and can interfere with shutil.rmtree.

Parameters 1

Name Type Default Description
dir_path Path

Directory to clean hidden files from

Returns

int

Number of hidden files removed

rmtree_robust
Remove directory tree with robust error handling for filesystem quirks. On macOS, shutil.rmtree ca…
3 None
def rmtree_robust(path: Path, max_retries: int = 3, caller: str | None = None) -> None

Remove directory tree with robust error handling for filesystem quirks.

On macOS, shutil.rmtree can fail with Errno 66 (Directory not empty) due to race conditions with Spotlight indexing, Finder metadata files (.DS_Store, ._*), or other processes briefly accessing the directory.

Strategy:

1. Try normal shutil.rmtree
2. On ENOTEMPTY, remove hidden files (.DS_Store, ._*) and retry
3. Fall back to subprocess `rm -rf` on macOS as last resort

Parameters 3

Name Type Default Description
path Path

Directory to remove

max_retries int 3

Number of retry attempts (default 3)

caller str | None None

Caller identifier for logging