File Operations

The file operations module provides utilities for file manipulation, copying, moving, and general file system operations.

Module Overview

Modern file operations for siege_utilities. Provides clean, type-safe file manipulation utilities.

siege_utilities.files.operations.sanitize_filename(name)[source]

Convert an arbitrary string into a safe filename component.

Unsafe characters (path separators, control characters, punctuation other than . and -) are replaced with underscores, runs of underscores are collapsed, and leading/trailing dots and underscores are trimmed. The result is always a non-empty string and the operation is idempotent (sanitize_filename(sanitize_filename(x)) == sanitize_filename(x)).

Parameters:

name (str) – Arbitrary input string.

Returns:

A filesystem-safe filename component.

Return type:

str

Raises:

TypeError – If name is not a string.

siege_utilities.files.operations.atomic_write_path(target)[source]

Context manager that yields a temporary path for writing, then atomically replaces the target on successful exit.

Usage:

with atomic_write_path("/data/output.csv") as tmp:
    df.to_csv(tmp, index=False)
# target is now atomically updated

If the block raises, the temp file is cleaned up and the target is left unchanged.

Parameters:

target (str | Path)

Return type:

Generator[Path, None, None]

siege_utilities.files.operations.atomic_write_shapefile(gdf, target, **kwargs)[source]

Write a GeoDataFrame as a Shapefile atomically.

Shapefiles produce sidecar files (.shx, .dbf, .prj, .cpg). This writes to a temp stem then renames all produced files to the final stem.

Parameters:

target (str | Path)

Return type:

None

siege_utilities.files.operations.remove_tree(path)[source]

Remove a file or directory tree safely.

SECURITY: Validates paths to prevent removing sensitive system files.

Parameters:

path (str | Path) – Path to file or directory to remove

Raises:
Return type:

None

Example

>>> remove_tree("temp_directory")
>>> remove_tree(Path("old_files"))
siege_utilities.files.operations.file_exists(path)[source]

Check if a file exists at the specified path.

SECURITY: This function validates paths to prevent path traversal attacks and access to sensitive system files.

Parameters:

path (str | Path) – Path to check

Returns:

True if file exists, False otherwise

Raises:

PathSecurityError – If path fails security validation

Return type:

bool

Example

>>> if file_exists("config.yaml"):
...     print("Config file found")
>>> file_exists("../../../etc/passwd")
Security Changes:
  • Now validates paths to block path traversal

  • Blocks access to sensitive system files

siege_utilities.files.operations.touch_file(path, create_parents=True)[source]

Create an empty file, creating parent directories if needed.

SECURITY: Validates paths to prevent path traversal attacks.

Parameters:
  • path (str | Path) – Path to the file to create

  • create_parents (bool) – Whether to create parent directories

Raises:
Return type:

None

Example

>>> touch_file("logs/app.log")
>>> touch_file(Path("data/output.txt"))
siege_utilities.files.operations.count_lines(file_path, encoding='utf-8')[source]

Count the total number of lines in a text file.

SECURITY: This function validates paths to prevent path traversal attacks and access to sensitive system files.

Parameters:
  • file_path (str | Path) – Path to the text file

  • encoding (str) – File encoding to use

Returns:

Number of lines

Raises:
Return type:

int

Example

>>> line_count = count_lines("data.csv")
>>> print(f"File has {line_count} lines")
siege_utilities.files.operations.copy_file(source, destination, overwrite=False)[source]

Copy a file from source to destination.

SECURITY: Validates both source and destination paths.

Parameters:
  • source (str | Path) – Source file path

  • destination (str | Path) – Destination file path

  • overwrite (bool) – Whether to overwrite existing files

Raises:
Return type:

None

Example

>>> copy_file("source.txt", "backup/source.txt")
>>> copy_file(Path("config.yaml"), Path("config.yaml.bak"))
siege_utilities.files.operations.move_file(source, destination, overwrite=False)[source]

Move a file from source to destination.

SECURITY: Validates both source and destination paths.

Parameters:
  • source (str | Path) – Source file path

  • destination (str | Path) – Destination file path

  • overwrite (bool) – Whether to overwrite existing files

Raises:
Return type:

None

Example

>>> move_file("temp.txt", "archive/temp.txt")
>>> move_file(Path("old.log"), Path("logs/old.log"))
siege_utilities.files.operations.get_file_size(file_path)[source]

Get the size of a file in bytes.

SECURITY: This function validates paths to prevent path traversal attacks and access to sensitive system files.

Parameters:

file_path (str | Path) – Path to the file

Returns:

File size in bytes

Raises:
Return type:

int

Example

>>> size = get_file_size("large_file.zip")
>>> print(f"File size: {size} bytes")
siege_utilities.files.operations.list_directory(path, pattern='*', include_dirs=True, include_files=True)[source]

List contents of a directory with optional filtering.

SECURITY: Validates directory path to prevent traversal attacks.

Parameters:
  • path (str | Path) – Directory path to list

  • pattern (str) – Glob pattern for filtering files

  • include_dirs (bool) – Whether to include directories

  • include_files (bool) – Whether to include files

Returns:

List of Path objects

Raises:
Return type:

List[Path]

Example

>>> files = list_directory("data", "*.csv")
>>> dirs = list_directory("logs", include_files=False)
siege_utilities.files.operations.run_command(command, cwd=None, timeout=30, capture_output=True, allow_list=None, unsafe=False)[source]

Run a shell command with security validation and proper error handling.

SECURITY: This function now validates commands by default. Only whitelisted commands are allowed unless unsafe=True is explicitly set.

Parameters:
  • command (str | List[str]) – Command to run (string or list)

  • cwd (str | Path | None) – Working directory for the command

  • timeout (int | None) – Timeout in seconds (default: 30)

  • capture_output (bool) – Whether to capture command output

  • allow_list (set | None) – Optional custom whitelist of allowed commands

  • unsafe (bool) – If True, bypass the allow-list check. The command is still executed via argv (shell=False) — string commands are shlex-split and run without shell expansion. Use this when you legitimately need a binary that isn’t in the allow-list, not when you need shell features.

Returns:

CompletedProcess object on success or non-zero exit.

Raises:
Return type:

CompletedProcess

Example

>>> result = run_command("ls -la")
>>> if result and result.returncode == 0:
...     print("Command succeeded")
>>> result = run_command("rm -rf /")
>>> result = run_command("git status", allow_list={'git', 'ls'})
>>> result = run_command("custom_cmd", unsafe=True)
Security Changes:
  • Previously used shell=True with no validation (CRITICAL VULNERABILITY)

  • Now validates all commands against whitelist by default

  • Blocks command injection, path traversal, sensitive file access

  • Use unsafe=True ONLY with trusted input in trusted environments

siege_utilities.files.operations.ensure_directory_exists(directory)[source]

Ensure a directory exists, creating it if necessary.

Parameters:

directory (str | Path) – Path to directory

Returns:

Path object for the directory

Raises:

PathSecurityError – If path fails security validation.

Return type:

Path

siege_utilities.files.operations.safe_file_write(file_path, content, encoding='utf-8', create_dirs=True)[source]

Safely write content to a file, optionally creating parent directories.

Parameters:
  • file_path (str | Path) – Path to file

  • content (str) – Content to write

  • encoding (str) – File encoding (default: utf-8)

  • create_dirs (bool) – Create parent directories if they don’t exist

Returns:

True if successful, False otherwise

Raises:

PathSecurityError – If path fails security validation.

Return type:

bool

siege_utilities.files.operations.safe_file_read(file_path, encoding='utf-8', default=None)[source]

Safely read content from a file.

Parameters:
  • file_path (str | Path) – Path to file

  • encoding (str) – File encoding (default: utf-8)

  • default (str | None) – Default value to return if file doesn’t exist or error occurs

Returns:

File content or default value

Raises:

PathSecurityError – If path fails security validation.

Return type:

str | None

siege_utilities.files.operations.safe_json_write(file_path, data, indent=2, create_dirs=True)[source]

Safely write data to a JSON file.

Parameters:
  • file_path (str | Path) – Path to JSON file

  • data (Any) – Data to write (must be JSON serializable)

  • indent (int) – JSON indentation (default: 2)

  • create_dirs (bool) – Create parent directories if they don’t exist

Returns:

True if successful, False otherwise

Raises:

PathSecurityError – If path fails security validation.

Return type:

bool

siege_utilities.files.operations.safe_json_read(file_path, default=None)[source]

Safely read data from a JSON file.

Parameters:
  • file_path (str | Path) – Path to JSON file

  • default (Any | None) – Default value to return if file doesn’t exist or error occurs

Returns:

Parsed JSON data or default value

Raises:

PathSecurityError – If path fails security validation.

Return type:

Any | None

siege_utilities.files.operations.get_file_size_mb(file_path)[source]

Get file size in megabytes.

Parameters:

file_path (str | Path) – Path to file

Returns:

File size in MB.

Raises:
Return type:

float

siege_utilities.files.operations.list_files_recursive(directory, pattern='*', exclude_dirs=True)[source]

List all files in a directory recursively.

Parameters:
  • directory (str | Path) – Directory to search

  • pattern (str) – Glob pattern (default: “*” for all files)

  • exclude_dirs (bool) – Exclude directories from results

Returns:

List of Path objects

Raises:
Return type:

list[Path]

siege_utilities.files.operations.rmtree(path)

Remove a file or directory tree safely.

SECURITY: Validates paths to prevent removing sensitive system files.

Parameters:

path (str | Path) – Path to file or directory to remove

Raises:
Return type:

None

Example

>>> remove_tree("temp_directory")
>>> remove_tree(Path("old_files"))
siege_utilities.files.operations.check_if_file_exists_at_path(path)[source]

Check if a file exists at the specified path.

Deprecated since version 3.19.0: Use file_exists() instead. Will be removed in v4.0.0.

Parameters:

path (str | Path)

Return type:

bool

siege_utilities.files.operations.delete_existing_file_and_replace_it_with_an_empty_file(file_path, create_parents=True)[source]

Backward compatibility function that deletes existing file and replaces it with empty file.

Deprecated since version 3.19.0: Use touch_file() for creating empty files. Note that touch_file does not delete the existing file first; if you need delete-then-create semantics, call Path.unlink() before touch_file(). Will be removed in v4.0.0.

SECURITY: Validates paths to prevent path traversal attacks.

Parameters:
  • file_path (str | Path) – Path to the file

  • create_parents (bool) – Whether to create parent directories

Raises:
Return type:

None

siege_utilities.files.operations.count_total_rows_in_file_pythonically(file_path, encoding='utf-8')

Count the total number of lines in a text file.

SECURITY: This function validates paths to prevent path traversal attacks and access to sensitive system files.

Parameters:
  • file_path (str | Path) – Path to the text file

  • encoding (str) – File encoding to use

Returns:

Number of lines

Raises:
Return type:

int

Example

>>> line_count = count_lines("data.csv")
>>> print(f"File has {line_count} lines")

Functions

siege_utilities.files.operations.copy_file(source, destination, overwrite=False)[source]

Copy a file from source to destination.

SECURITY: Validates both source and destination paths.

Parameters:
  • source (str | Path) – Source file path

  • destination (str | Path) – Destination file path

  • overwrite (bool) – Whether to overwrite existing files

Raises:
Return type:

None

Example

>>> copy_file("source.txt", "backup/source.txt")
>>> copy_file(Path("config.yaml"), Path("config.yaml.bak"))
siege_utilities.files.operations.move_file(source, destination, overwrite=False)[source]

Move a file from source to destination.

SECURITY: Validates both source and destination paths.

Parameters:
  • source (str | Path) – Source file path

  • destination (str | Path) – Destination file path

  • overwrite (bool) – Whether to overwrite existing files

Raises:
Return type:

None

Example

>>> move_file("temp.txt", "archive/temp.txt")
>>> move_file(Path("old.log"), Path("logs/old.log"))

Usage Examples

Basic file operations:

import siege_utilities

# Copy a file
siege_utilities.copy_file('source.txt', 'destination.txt')

# Move a file
siege_utilities.move_file('old_name.txt', 'new_name.txt')

# Delete a file
siege_utilities.delete_file('unwanted_file.txt')

# Create directory
siege_utilities.create_directory('new_folder')

# List files in directory
files = siege_utilities.list_files('data_folder')
print(f"Found {len(files)} files")

Batch file processing:

import os

# Process multiple files
source_dir = 'input_files'
output_dir = 'processed_files'

# Create output directory
siege_utilities.create_directory(output_dir)

# Process all files
for filename in siege_utilities.list_files(source_dir):
    if filename.endswith('.txt'):
        source_path = os.path.join(source_dir, filename)
        dest_path = os.path.join(output_dir, f"processed_{filename}")
        siege_utilities.copy_file(source_path, dest_path)

Unit Tests

The file operations module has comprehensive test coverage:

✅ test_file_operations.py - All file operation tests pass

Test Coverage:
- File copying with various scenarios
- File moving and renaming
- File deletion with safety checks
- Directory creation and management
- File listing and discovery
- Error handling for missing files
- Permission handling

Test Results: All file operation tests pass successfully with comprehensive coverage.