A formatter for Python code that applies consistent formatting rules based on configurable style guidelines
YAPF's file operations module provides utilities for reading Python files with proper encoding detection, writing formatted code, managing configuration files, and handling exclude patterns for batch operations.
Read Python files with automatic encoding detection and line ending preservation.
def ReadFile(filename, logger=None):
"""
Read the contents of a file with encoding detection.
Args:
filename (str): Path to the file to read
logger: Optional function for logging errors
Returns:
tuple: (source, line_ending, encoding)
- source (str): File contents with normalized line endings
- line_ending (str): Original line ending style
- encoding (str): Detected file encoding
Raises:
IOError: If file cannot be read
UnicodeDecodeError: If file cannot be decoded
"""
def FileEncoding(filename):
"""
Detect the encoding of a Python file.
Args:
filename (str): Path to the file
Returns:
str: Detected encoding (e.g., 'utf-8', 'latin-1')
"""
def LineEnding(lines):
"""
Determine the line ending style of file lines.
Args:
lines (list): List of lines from file
Returns:
str: Line ending style ('\\n', '\\r\\n', '\\r')
"""Write formatted code back to files with proper encoding and line ending handling.
def WriteReformattedCode(filename, reformatted_code, encoding='', in_place=False):
"""
Write reformatted code to file or stdout.
Args:
filename (str): File path or '<stdout>' for stdout
reformatted_code (str): The formatted code to write
encoding (str): File encoding to use (default: '')
in_place (bool): Whether to write to the original file
"""Find Python files for formatting operations with support for exclusion patterns.
def GetCommandLineFiles(command_line_file_list, recursive, exclude):
"""
Get list of Python files from command line arguments.
Args:
command_line_file_list (list): List of file and directory paths
recursive (bool): Whether to search directories recursively
exclude (list): Patterns for files to exclude
Returns:
list: List of Python file paths to process
"""
def IsPythonFile(filename):
"""
Check if a file is a Python file.
Args:
filename (str): Path to file to check
Returns:
bool: True if file is a Python file
"""
def IsIgnored(path, exclude):
"""
Check if a path matches any exclude pattern.
Args:
path (str): Path to check
exclude (list): List of exclude patterns
Returns:
bool: True if path should be ignored
"""Find and load YAPF configuration files from project directories.
def GetDefaultStyleForDir(directory):
"""
Get the default style configuration for a directory.
Searches up the directory tree for:
- .style.yapf files
- pyproject.toml with [tool.yapf] section
- setup.cfg with [yapf] section
Args:
directory (str): Directory to search from
Returns:
str: Path to configuration file or style name
"""
def GetExcludePatternsForDir(directory):
"""
Get exclude patterns from .yapfignore files.
Args:
directory (str): Directory to search from
Returns:
list: List of glob patterns to exclude
"""from yapf.yapflib.yapf_api import ReadFile
import logging
# Read file with automatic encoding detection
try:
source, line_ending, encoding = ReadFile('my_script.py')
print(f"File encoding: {encoding}")
print(f"Line ending: {repr(line_ending)}")
print(f"File length: {len(source)} characters")
except (IOError, UnicodeDecodeError) as e:
print(f"Error reading file: {e}")
# Read with error logging
def log_error(message):
logging.error(f"File read error: {message}")
source, line_ending, encoding = ReadFile(
'my_script.py',
logger=log_error
)from yapf.yapflib import file_resources
# Write to stdout
file_resources.WriteReformattedCode(
'<stdout>',
formatted_code,
encoding='utf-8'
)
# Write to specific file
file_resources.WriteReformattedCode(
'output.py',
formatted_code,
encoding='utf-8',
in_place=True
)from yapf.yapflib import file_resources
# Get all Python files in current directory
files = file_resources.GetCommandLineFiles(
['.'],
recursive=False,
exclude_patterns=[]
)
# Get files recursively, excluding test files
files = file_resources.GetCommandLineFiles(
['src/', 'lib/'],
recursive=True,
exclude_patterns=['*test*.py', '*_test.py']
)
print(f"Found {len(files)} Python files")
for file in files:
print(f" {file}")from yapf.yapflib import file_resources
import os
# Find default style for current project
current_dir = os.getcwd()
style_config = file_resources.GetDefaultStyleForDir(current_dir)
print(f"Default style: {style_config}")
# Get exclude patterns from .yapfignore
exclude_patterns = file_resources.GetExcludePatternsForDir(current_dir)
print(f"Exclude patterns: {exclude_patterns}")
# Search from specific directory
project_root = '/path/to/my/project'
style_config = file_resources.GetDefaultStyleForDir(project_root)import os
from yapf.yapflib import file_resources
from yapf.yapflib.yapf_api import FormatFile
def format_project(directory, style='pep8', exclude_patterns=None):
"""
Format all Python files in a project directory.
Args:
directory (str): Root directory to process
style (str): Style configuration to use
exclude_patterns (list): Patterns for files to exclude
"""
if exclude_patterns is None:
exclude_patterns = []
# Discover configuration
local_style = file_resources.GetDefaultStyleForDir(directory)
if local_style:
style = local_style
print(f"Using local style: {style}")
# Get exclude patterns from .yapfignore
ignore_patterns = file_resources.GetExcludePatternsForDir(directory)
exclude_patterns.extend(ignore_patterns)
# Find all Python files
files = file_resources.GetCommandLineFiles(
[directory],
recursive=True,
exclude_patterns=exclude_patterns
)
print(f"Found {len(files)} Python files to format")
# Format each file
modified_count = 0
for filepath in files:
try:
_, _, changed = FormatFile(
filepath,
style_config=style,
in_place=True
)
if changed:
modified_count += 1
print(f" Formatted: {filepath}")
except Exception as e:
print(f" Error formatting {filepath}: {e}")
print(f"Modified {modified_count} files")
# Usage
format_project('/path/to/my/project', style='google')from yapf.yapflib import file_resources
from yapf.yapflib.yapf_api import ReadFile, FormatCode
def safe_format_file(filepath):
"""
Safely format a file preserving encoding and line endings.
Args:
filepath (str): Path to file to format
Returns:
bool: True if file was modified
"""
try:
# Read with encoding detection
source, line_ending, encoding = ReadFile(filepath)
print(f"File: {filepath}")
print(f" Encoding: {encoding}")
print(f" Line ending: {repr(line_ending)}")
# Format the code
formatted, changed = FormatCode(source, filename=filepath)
if changed:
# Restore original line endings
if line_ending != '\n':
formatted = formatted.replace('\n', line_ending)
# Write back with original encoding
file_resources.WriteReformattedCode(
filepath,
formatted,
encoding=encoding,
in_place=True
)
print(f" Formatted and saved")
return True
else:
print(f" No changes needed")
return False
except Exception as e:
print(f" Error: {e}")
return False
# Process multiple files
files = ['script1.py', 'script2.py', 'module.py']
for filepath in files:
safe_format_file(filepath)Create a .yapfignore file in your project root:
# .yapfignore - patterns for files to exclude from formatting
# Generated files
*_pb2.py
*_pb2_grpc.py
# Third-party code
vendor/
external/
# Test fixtures
*/test_data/*
test_fixtures/
# Specific files
legacy_code.pyThen use it in your code:
from yapf.yapflib import file_resources
# Exclude patterns are automatically loaded
exclude_patterns = file_resources.GetExcludePatternsForDir('.')
print("Exclude patterns:", exclude_patterns)
# Use in file discovery
files = file_resources.GetCommandLineFiles(
['.'],
recursive=True,
exclude_patterns=exclude_patterns
)File operations can raise various exceptions that should be handled appropriately:
from yapf.yapflib.yapf_api import ReadFile
from yapf.yapflib import file_resources
def robust_file_processing(filepath):
"""Process a file with comprehensive error handling."""
try:
source, line_ending, encoding = ReadFile(filepath)
return source, line_ending, encoding
except IOError as e:
print(f"Cannot read file {filepath}: {e}")
return None, None, None
except UnicodeDecodeError as e:
print(f"Cannot decode file {filepath}: {e}")
print("File may not be a valid Python file or uses unsupported encoding")
return None, None, None
except Exception as e:
print(f"Unexpected error reading {filepath}: {e}")
return None, None, None# Line ending constants
CR = '\r'
LF = '\n'
CRLF = '\r\n'Install with Tessl CLI
npx tessl i tessl/pypi-yapf