0
# File Operations
1
2
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.
3
4
## Capabilities
5
6
### File Reading
7
8
Read Python files with automatic encoding detection and line ending preservation.
9
10
```python { .api }
11
def ReadFile(filename, logger=None):
12
"""
13
Read the contents of a file with encoding detection.
14
15
Args:
16
filename (str): Path to the file to read
17
logger: Optional function for logging errors
18
19
Returns:
20
tuple: (source, line_ending, encoding)
21
- source (str): File contents with normalized line endings
22
- line_ending (str): Original line ending style
23
- encoding (str): Detected file encoding
24
25
Raises:
26
IOError: If file cannot be read
27
UnicodeDecodeError: If file cannot be decoded
28
"""
29
30
def FileEncoding(filename):
31
"""
32
Detect the encoding of a Python file.
33
34
Args:
35
filename (str): Path to the file
36
37
Returns:
38
str: Detected encoding (e.g., 'utf-8', 'latin-1')
39
"""
40
41
def LineEnding(lines):
42
"""
43
Determine the line ending style of file lines.
44
45
Args:
46
lines (list): List of lines from file
47
48
Returns:
49
str: Line ending style ('\\n', '\\r\\n', '\\r')
50
"""
51
```
52
53
### File Writing
54
55
Write formatted code back to files with proper encoding and line ending handling.
56
57
```python { .api }
58
def WriteReformattedCode(filename, reformatted_code, encoding='', in_place=False):
59
"""
60
Write reformatted code to file or stdout.
61
62
Args:
63
filename (str): File path or '<stdout>' for stdout
64
reformatted_code (str): The formatted code to write
65
encoding (str): File encoding to use (default: '')
66
in_place (bool): Whether to write to the original file
67
"""
68
```
69
70
### File Discovery
71
72
Find Python files for formatting operations with support for exclusion patterns.
73
74
```python { .api }
75
def GetCommandLineFiles(command_line_file_list, recursive, exclude):
76
"""
77
Get list of Python files from command line arguments.
78
79
Args:
80
command_line_file_list (list): List of file and directory paths
81
recursive (bool): Whether to search directories recursively
82
exclude (list): Patterns for files to exclude
83
84
Returns:
85
list: List of Python file paths to process
86
"""
87
88
def IsPythonFile(filename):
89
"""
90
Check if a file is a Python file.
91
92
Args:
93
filename (str): Path to file to check
94
95
Returns:
96
bool: True if file is a Python file
97
"""
98
99
def IsIgnored(path, exclude):
100
"""
101
Check if a path matches any exclude pattern.
102
103
Args:
104
path (str): Path to check
105
exclude (list): List of exclude patterns
106
107
Returns:
108
bool: True if path should be ignored
109
"""
110
```
111
112
### Configuration Discovery
113
114
Find and load YAPF configuration files from project directories.
115
116
```python { .api }
117
def GetDefaultStyleForDir(directory):
118
"""
119
Get the default style configuration for a directory.
120
121
Searches up the directory tree for:
122
- .style.yapf files
123
- pyproject.toml with [tool.yapf] section
124
- setup.cfg with [yapf] section
125
126
Args:
127
directory (str): Directory to search from
128
129
Returns:
130
str: Path to configuration file or style name
131
"""
132
133
def GetExcludePatternsForDir(directory):
134
"""
135
Get exclude patterns from .yapfignore files.
136
137
Args:
138
directory (str): Directory to search from
139
140
Returns:
141
list: List of glob patterns to exclude
142
"""
143
```
144
145
## Usage Examples
146
147
### Reading Files with Encoding Detection
148
149
```python
150
from yapf.yapflib.yapf_api import ReadFile
151
import logging
152
153
# Read file with automatic encoding detection
154
try:
155
source, line_ending, encoding = ReadFile('my_script.py')
156
print(f"File encoding: {encoding}")
157
print(f"Line ending: {repr(line_ending)}")
158
print(f"File length: {len(source)} characters")
159
except (IOError, UnicodeDecodeError) as e:
160
print(f"Error reading file: {e}")
161
162
# Read with error logging
163
def log_error(message):
164
logging.error(f"File read error: {message}")
165
166
source, line_ending, encoding = ReadFile(
167
'my_script.py',
168
logger=log_error
169
)
170
```
171
172
### Writing Formatted Code
173
174
```python
175
from yapf.yapflib import file_resources
176
177
# Write to stdout
178
file_resources.WriteReformattedCode(
179
'<stdout>',
180
formatted_code,
181
encoding='utf-8'
182
)
183
184
# Write to specific file
185
file_resources.WriteReformattedCode(
186
'output.py',
187
formatted_code,
188
encoding='utf-8',
189
in_place=True
190
)
191
```
192
193
### File Discovery for Batch Operations
194
195
```python
196
from yapf.yapflib import file_resources
197
198
# Get all Python files in current directory
199
files = file_resources.GetCommandLineFiles(
200
['.'],
201
recursive=False,
202
exclude_patterns=[]
203
)
204
205
# Get files recursively, excluding test files
206
files = file_resources.GetCommandLineFiles(
207
['src/', 'lib/'],
208
recursive=True,
209
exclude_patterns=['*test*.py', '*_test.py']
210
)
211
212
print(f"Found {len(files)} Python files")
213
for file in files:
214
print(f" {file}")
215
```
216
217
### Configuration File Discovery
218
219
```python
220
from yapf.yapflib import file_resources
221
import os
222
223
# Find default style for current project
224
current_dir = os.getcwd()
225
style_config = file_resources.GetDefaultStyleForDir(current_dir)
226
print(f"Default style: {style_config}")
227
228
# Get exclude patterns from .yapfignore
229
exclude_patterns = file_resources.GetExcludePatternsForDir(current_dir)
230
print(f"Exclude patterns: {exclude_patterns}")
231
232
# Search from specific directory
233
project_root = '/path/to/my/project'
234
style_config = file_resources.GetDefaultStyleForDir(project_root)
235
```
236
237
### Complete File Processing Pipeline
238
239
```python
240
import os
241
from yapf.yapflib import file_resources
242
from yapf.yapflib.yapf_api import FormatFile
243
244
def format_project(directory, style='pep8', exclude_patterns=None):
245
"""
246
Format all Python files in a project directory.
247
248
Args:
249
directory (str): Root directory to process
250
style (str): Style configuration to use
251
exclude_patterns (list): Patterns for files to exclude
252
"""
253
if exclude_patterns is None:
254
exclude_patterns = []
255
256
# Discover configuration
257
local_style = file_resources.GetDefaultStyleForDir(directory)
258
if local_style:
259
style = local_style
260
print(f"Using local style: {style}")
261
262
# Get exclude patterns from .yapfignore
263
ignore_patterns = file_resources.GetExcludePatternsForDir(directory)
264
exclude_patterns.extend(ignore_patterns)
265
266
# Find all Python files
267
files = file_resources.GetCommandLineFiles(
268
[directory],
269
recursive=True,
270
exclude_patterns=exclude_patterns
271
)
272
273
print(f"Found {len(files)} Python files to format")
274
275
# Format each file
276
modified_count = 0
277
for filepath in files:
278
try:
279
_, _, changed = FormatFile(
280
filepath,
281
style_config=style,
282
in_place=True
283
)
284
if changed:
285
modified_count += 1
286
print(f" Formatted: {filepath}")
287
except Exception as e:
288
print(f" Error formatting {filepath}: {e}")
289
290
print(f"Modified {modified_count} files")
291
292
# Usage
293
format_project('/path/to/my/project', style='google')
294
```
295
296
### Encoding and Line Ending Handling
297
298
```python
299
from yapf.yapflib import file_resources
300
from yapf.yapflib.yapf_api import ReadFile, FormatCode
301
302
def safe_format_file(filepath):
303
"""
304
Safely format a file preserving encoding and line endings.
305
306
Args:
307
filepath (str): Path to file to format
308
309
Returns:
310
bool: True if file was modified
311
"""
312
try:
313
# Read with encoding detection
314
source, line_ending, encoding = ReadFile(filepath)
315
print(f"File: {filepath}")
316
print(f" Encoding: {encoding}")
317
print(f" Line ending: {repr(line_ending)}")
318
319
# Format the code
320
formatted, changed = FormatCode(source, filename=filepath)
321
322
if changed:
323
# Restore original line endings
324
if line_ending != '\n':
325
formatted = formatted.replace('\n', line_ending)
326
327
# Write back with original encoding
328
file_resources.WriteReformattedCode(
329
filepath,
330
formatted,
331
encoding=encoding,
332
in_place=True
333
)
334
print(f" Formatted and saved")
335
return True
336
else:
337
print(f" No changes needed")
338
return False
339
340
except Exception as e:
341
print(f" Error: {e}")
342
return False
343
344
# Process multiple files
345
files = ['script1.py', 'script2.py', 'module.py']
346
for filepath in files:
347
safe_format_file(filepath)
348
```
349
350
### Working with .yapfignore Files
351
352
Create a `.yapfignore` file in your project root:
353
354
```
355
# .yapfignore - patterns for files to exclude from formatting
356
357
# Generated files
358
*_pb2.py
359
*_pb2_grpc.py
360
361
# Third-party code
362
vendor/
363
external/
364
365
# Test fixtures
366
*/test_data/*
367
test_fixtures/
368
369
# Specific files
370
legacy_code.py
371
```
372
373
Then use it in your code:
374
375
```python
376
from yapf.yapflib import file_resources
377
378
# Exclude patterns are automatically loaded
379
exclude_patterns = file_resources.GetExcludePatternsForDir('.')
380
print("Exclude patterns:", exclude_patterns)
381
382
# Use in file discovery
383
files = file_resources.GetCommandLineFiles(
384
['.'],
385
recursive=True,
386
exclude_patterns=exclude_patterns
387
)
388
```
389
390
## Error Handling
391
392
File operations can raise various exceptions that should be handled appropriately:
393
394
```python
395
from yapf.yapflib.yapf_api import ReadFile
396
from yapf.yapflib import file_resources
397
398
def robust_file_processing(filepath):
399
"""Process a file with comprehensive error handling."""
400
try:
401
source, line_ending, encoding = ReadFile(filepath)
402
return source, line_ending, encoding
403
404
except IOError as e:
405
print(f"Cannot read file {filepath}: {e}")
406
return None, None, None
407
408
except UnicodeDecodeError as e:
409
print(f"Cannot decode file {filepath}: {e}")
410
print("File may not be a valid Python file or uses unsupported encoding")
411
return None, None, None
412
413
except Exception as e:
414
print(f"Unexpected error reading {filepath}: {e}")
415
return None, None, None
416
```
417
418
## Constants
419
420
```python { .api }
421
# Line ending constants
422
CR = '\r'
423
LF = '\n'
424
CRLF = '\r\n'
425
```