View on GitHub

Utilities Module API

The utils module provides helper functions for file and path management.

Module: file_utils.py

File system utilities and project path management.

Functions

get_project_paths()

Get standardized project directory paths.

Parameters: None

Returns:

Structure:

{
    'root': Path('/path/to/OpenTestability'),
    'data': Path('/path/to/OpenTestability/data'),
    'input': Path('/path/to/OpenTestability/data/input'),
    'parsed': Path('/path/to/OpenTestability/data/parsed'),
    'dag_output': Path('/path/to/OpenTestability/data/dag_output'),
    'results': Path('/path/to/OpenTestability/data/results'),
    'reconvergence_output': Path('/path/to/OpenTestability/data/reconvergence_output'),
    'graphs': Path('/path/to/OpenTestability/data/graphs')
}

Example:

from opentestability.utils.file_utils import get_project_paths

paths = get_project_paths()
input_dir = paths['input']
output_dir = paths['parsed']

print(f"Input directory: {input_dir}")
print(f"Parsed directory: {output_dir}")

# Use paths for file operations
input_file = input_dir / "circuit.v"
output_file = output_dir / "circuit.json"

ensure_directory(directory_path)

Ensure a directory exists, creating it if necessary.

Parameters:

Returns: None

Side Effects:

Example:

from opentestability.utils.file_utils import ensure_directory, get_project_paths
from pathlib import Path

paths = get_project_paths()

# Ensure standard directory exists
ensure_directory(paths['parsed'])

# Ensure custom subdirectory exists
custom_dir = paths['results'] / 'experiments' / 'test1'
ensure_directory(custom_dir)

# Works with string paths too
ensure_directory("/path/to/custom/output")

Usage Patterns

Standard Project Setup

from opentestability.utils.file_utils import get_project_paths, ensure_directory

# Get project paths
paths = get_project_paths()

# Ensure all directories exist
for name, path in paths.items():
    if name != 'root':  # Skip root directory
        ensure_directory(path)

print("Project structure ready!")

File Path Construction

from opentestability.utils.file_utils import get_project_paths

paths = get_project_paths()

# Construct input file path
input_file = paths['input'] / "serial_ALU.v"

# Construct output file path
output_file = paths['parsed'] / f"{input_file.stem}_parsed.json"

# Check if file exists
if input_file.exists():
    print(f"Processing {input_file.name}")
else:
    print(f"File not found: {input_file}")

Multiple Output Locations

from opentestability.utils.file_utils import get_project_paths, ensure_directory

paths = get_project_paths()

# Create timestamped output directory
from datetime import datetime
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
experiment_dir = paths['results'] / f"experiment_{timestamp}"
ensure_directory(experiment_dir)

# Save results
output_file = experiment_dir / "scoap_results.json"

Integration Examples

With Parser Module

from opentestability.utils.file_utils import get_project_paths, ensure_directory
from opentestability.parsers.verilog_parser import parse_verilog_netlist
import json

paths = get_project_paths()

# Input and output paths
input_path = paths['input'] / "circuit.v"
output_dir = paths['parsed']
ensure_directory(output_dir)

# Parse
modules = parse_verilog_netlist(str(input_path))

# Save to custom location
output_path = output_dir / "circuit_parsed.json"
with open(output_path, 'w') as f:
    json.dump(modules, f, indent=2)

With Core Modules

from opentestability.utils.file_utils import get_project_paths
from opentestability.core.dag_builder import create_dag_from_netlist
from opentestability.core.scoap import run as scoap_run

paths = get_project_paths()

# All modules use get_project_paths internally
# So they work together seamlessly
dag_file = "circuit_parsed.json"
create_dag_from_netlist(dag_file)
# DAG saved to paths['dag_output']

scoap_run("circuit.txt", "scoap.json", json_flag=True)
# Results saved to paths['results']

Custom Directory Structure

from opentestability.utils.file_utils import get_project_paths, ensure_directory

paths = get_project_paths()

# Create custom experiment structure
experiment_name = "fanout_analysis"
experiment_root = paths['results'] / experiment_name

subdirs = ['raw_data', 'processed', 'visualizations', 'reports']
experiment_paths = {}

for subdir in subdirs:
    dir_path = experiment_root / subdir
    ensure_directory(dir_path)
    experiment_paths[subdir] = dir_path

print(f"Experiment structure created: {experiment_root}")

Path Resolution

Relative vs Absolute Paths

from opentestability.utils.file_utils import get_project_paths

paths = get_project_paths()

# All returned paths are absolute
print(f"Root (absolute): {paths['root']}")
print(f"Is absolute: {paths['root'].is_absolute()}")  # True

# Convert to relative path
from pathlib import Path
relative = Path('data') / 'input'
absolute = paths['root'] / relative
print(f"Absolute path: {absolute}")

Cross-Platform Compatibility

from opentestability.utils.file_utils import get_project_paths
from pathlib import Path

paths = get_project_paths()

# Path objects work across platforms (Windows, Linux, macOS)
# Use forward slashes in code, Path handles conversion
input_file = paths['input'] / "circuit.v"

# Works on:
# - Windows: C:\Users\...\OpenTestability\data\input\circuit.v
# - Linux:   /home/.../OpenTestability/data/input/circuit.v
# - macOS:   /Users/.../OpenTestability/data/input/circuit.v

Best Practices

Always Use get_project_paths()

# Good: Portable and consistent
from opentestability.utils.file_utils import get_project_paths
paths = get_project_paths()
input_file = paths['input'] / "circuit.v"

# Bad: Hardcoded paths
input_file = "/home/user/OpenTestability/data/input/circuit.v"  # Not portable!

Check Before Operating

from opentestability.utils.file_utils import get_project_paths

paths = get_project_paths()
input_file = paths['input'] / "circuit.v"

if input_file.exists():
    # Proceed with operation
    with open(input_file, 'r') as f:
        content = f.read()
else:
    raise FileNotFoundError(f"Required file not found: {input_file}")

Create Directories Before Writing

from opentestability.utils.file_utils import get_project_paths, ensure_directory

paths = get_project_paths()
output_dir = paths['results'] / 'custom_analysis'

# Always ensure directory exists before writing
ensure_directory(output_dir)

output_file = output_dir / "results.json"
with open(output_file, 'w') as f:
    json.dump(data, f)

Error Handling

Path Not Found

from opentestability.utils.file_utils import get_project_paths
from pathlib import Path

paths = get_project_paths()

try:
    input_file = paths['input'] / "circuit.v"
    if not input_file.exists():
        raise FileNotFoundError(f"Input file not found: {input_file}")
    
    # Process file
    with open(input_file, 'r') as f:
        data = f.read()
        
except FileNotFoundError as e:
    print(f"Error: {e}")
    print(f"Please place your Verilog files in: {paths['input']}")

Permission Errors

from opentestability.utils.file_utils import ensure_directory
import os

try:
    ensure_directory("/restricted/path")
except PermissionError as e:
    print(f"Permission denied: {e}")
    print("Please choose a writable location")

Advanced Usage

Listing Files in Directories

from opentestability.utils.file_utils import get_project_paths

paths = get_project_paths()

# List all Verilog files
verilog_files = list(paths['input'].glob("*.v"))
print(f"Found {len(verilog_files)} Verilog files:")
for vfile in verilog_files:
    print(f"  - {vfile.name}")

# List all JSON files recursively
json_files = list(paths['parsed'].rglob("*.json"))
print(f"\nFound {len(json_files)} JSON files (recursive)")

File Size and Modification Time

from opentestability.utils.file_utils import get_project_paths
from datetime import datetime

paths = get_project_paths()

for json_file in paths['parsed'].glob("*.json"):
    size = json_file.stat().st_size
    mtime = datetime.fromtimestamp(json_file.stat().st_mtime)
    
    print(f"{json_file.name}:")
    print(f"  Size: {size:,} bytes")
    print(f"  Modified: {mtime.strftime('%Y-%m-%d %H:%M:%S')}")

Backup and Cleanup

from opentestability.utils.file_utils import get_project_paths
import shutil
from datetime import datetime

paths = get_project_paths()

# Create backup
backup_dir = paths['root'] / 'backups' / datetime.now().strftime("%Y%m%d")
ensure_directory(backup_dir)

for result_file in paths['results'].glob("*.json"):
    backup_file = backup_dir / result_file.name
    shutil.copy2(result_file, backup_file)

print(f"Backed up results to: {backup_dir}")

# Clean old temporary files
temp_files = paths['data'].glob("*.tmp")
for tmp in temp_files:
    tmp.unlink()

Configuration

Custom Project Root

By default, get_project_paths() finds the project root automatically. To use a custom root:

from pathlib import Path
import os

# Set custom project root (before importing other modules)
os.environ['OPENTESTABILITY_ROOT'] = '/path/to/custom/location'

from opentestability.utils.file_utils import get_project_paths

paths = get_project_paths()
# Now uses custom root

Adding New Directory Types

To extend with custom directories:

from opentestability.utils.file_utils import get_project_paths, ensure_directory

paths = get_project_paths()

# Add custom paths
paths['experiments'] = paths['data'] / 'experiments'
paths['reports'] = paths['data'] / 'reports'
paths['temp'] = paths['data'] / 'temp'

# Ensure they exist
for key in ['experiments', 'reports', 'temp']:
    ensure_directory(paths[key])

Dependencies

Required Packages

No External Dependencies

The utils module has no external dependencies, making it lightweight and always available.


Performance

Path Operations

Best Practices

# Good: Call once, reuse
paths = get_project_paths()
for file in input_files:
    process(paths['input'] / file)

# Less optimal: Multiple calls
for file in input_files:
    paths = get_project_paths()  # Unnecessary repeated calls
    process(paths['input'] / file)

Complete Example

Full Project Workflow with Utils

from opentestability.utils.file_utils import get_project_paths, ensure_directory
from opentestability.parsers.verilog_parser import parse
from opentestability.core.dag_builder import create_dag_from_netlist
from opentestability.core.scoap import run as scoap_run
from opentestability.visualization.graph_renderer import visualize_gate_graph
import json

# Setup paths
paths = get_project_paths()
print(f"Working in: {paths['root']}")

# Ensure all directories exist
for name, path in paths.items():
    if name != 'root':
        ensure_directory(path)

# Find input files
verilog_files = list(paths['input'].glob("*.v"))
print(f"\nFound {len(verilog_files)} Verilog files")

# Process each file
for vfile in verilog_files:
    print(f"\nProcessing {vfile.name}...")
    
    # Parse
    basename = vfile.stem
    parse(vfile.name, f"{basename}.txt")
    
    # Create DAG
    create_dag_from_netlist(f"{basename}_parsed.json")
    
    # SCOAP analysis
    scoap_run(f"{basename}.txt", f"{basename}_scoap.json", json_flag=True)
    
    # Visualize
    visualize_gate_graph(f"{basename}_dag.json")
    
    print(f"✓ Completed {basename}")

print("\nAll files processed!")
print(f"Results in: {paths['results']}")
print(f"Graphs in: {paths['graphs']}")