0
# File and Directory Operations
1
2
Comprehensive file I/O operations and directory management capabilities including reading, writing, seeking, directory traversal, and extended attribute support. These operations provide both high-level Python semantics and low-level control over filesystem behavior.
3
4
## Capabilities
5
6
### FileHandle Class
7
8
Raw I/O file handle that extends Python's `io.RawIOBase` for direct binary file operations with LittleFS-specific optimizations.
9
10
```python { .api }
11
class FileHandle(io.RawIOBase):
12
def __init__(self, fs: LFSFilesystem, fh: LFSFile):
13
"""
14
Initialize file handle wrapper.
15
16
Parameters:
17
- fs: LFSFilesystem, filesystem object
18
- fh: LFSFile, low-level file handle
19
"""
20
21
def close(self) -> None:
22
"""
23
Close the file handle.
24
LittleFS automatically flushes on close.
25
"""
26
27
def readable(self) -> bool:
28
"""
29
Check if file is readable.
30
31
Returns:
32
bool: True if file was opened for reading
33
"""
34
35
def writable(self) -> bool:
36
"""
37
Check if file is writable.
38
39
Returns:
40
bool: True if file was opened for writing
41
"""
42
43
def seekable(self) -> bool:
44
"""
45
Check if file supports seeking.
46
47
Returns:
48
bool: Always True for LittleFS files
49
"""
50
51
def seek(self, offset: int, whence: int = io.SEEK_SET) -> int:
52
"""
53
Seek to position in file.
54
55
Parameters:
56
- offset: int, byte offset
57
- whence: int, seek reference (SEEK_SET, SEEK_CUR, SEEK_END)
58
59
Returns:
60
int: New absolute position
61
"""
62
63
def tell(self) -> int:
64
"""
65
Get current file position.
66
67
Returns:
68
int: Current byte position
69
"""
70
71
def truncate(self, size: int = None) -> int:
72
"""
73
Truncate file to specified size.
74
75
Parameters:
76
- size: int, new file size (default: current position)
77
78
Returns:
79
int: New file size
80
"""
81
82
def write(self, data: bytes) -> int:
83
"""
84
Write data to file.
85
86
Parameters:
87
- data: bytes, data to write
88
89
Returns:
90
int: Number of bytes written
91
"""
92
93
def readinto(self, b: bytearray) -> int:
94
"""
95
Read data into existing buffer.
96
97
Parameters:
98
- b: bytearray, buffer to read into
99
100
Returns:
101
int: Number of bytes read
102
"""
103
104
def readall(self) -> bytes:
105
"""
106
Read all remaining data from current position.
107
108
Returns:
109
bytes: All data from current position to end of file
110
"""
111
112
def flush(self) -> None:
113
"""
114
Flush file buffers to storage.
115
"""
116
```
117
118
### File I/O Operations
119
120
High-level file operations integrated with Python's I/O system.
121
122
```python { .api }
123
# File opening with Python semantics (from LittleFS class)
124
def open(self, fname: str, mode="r", buffering: int = -1, encoding: str = None,
125
errors: str = None, newline: str = None):
126
"""
127
Open file with full Python I/O stack integration.
128
129
Mode combinations:
130
- 'r': read only (default)
131
- 'w': write only, truncate existing
132
- 'a': write only, append to end
133
- 'x': write only, fail if exists
134
- '+': read and write
135
- 'b': binary mode
136
- 't': text mode (default)
137
138
Returns TextIOWrapper, BufferedReader, BufferedWriter, or FileHandle
139
based on mode and buffering settings.
140
"""
141
```
142
143
### Directory Management
144
145
Directory creation, listing, and navigation operations.
146
147
```python { .api }
148
# High-level directory operations (from LittleFS class)
149
def listdir(self, path=".") -> List[str]:
150
"""List directory contents as filename strings."""
151
152
def scandir(self, path="."):
153
"""Scan directory yielding LFSStat objects with full metadata."""
154
155
def mkdir(self, path: str) -> int:
156
"""Create single directory."""
157
158
def makedirs(self, name: str, exist_ok=False):
159
"""Create directory tree recursively."""
160
161
def rmdir(self, path: str) -> int:
162
"""Remove empty directory."""
163
164
def removedirs(self, name):
165
"""Remove directory tree upward while empty."""
166
167
def walk(self, top: str):
168
"""Walk directory tree yielding (root, dirs, files) tuples."""
169
170
# Low-level directory operations (from lfs module)
171
def dir_open(fs: LFSFilesystem, path: str) -> LFSDirectory:
172
"""Open directory for manual iteration."""
173
174
def dir_read(fs: LFSFilesystem, dh: LFSDirectory) -> Optional[LFSStat]:
175
"""Read next directory entry, None when exhausted."""
176
177
def dir_close(fs: LFSFilesystem, dh: LFSDirectory) -> int:
178
"""Close directory handle."""
179
180
def dir_tell(fs: LFSFilesystem, dh: LFSDirectory) -> int:
181
"""Get current directory iteration position."""
182
183
def dir_rewind(fs: LFSFilesystem, dh: LFSDirectory) -> int:
184
"""Reset directory iteration to beginning."""
185
```
186
187
### Path Operations
188
189
File and directory manipulation at the path level.
190
191
```python { .api }
192
# High-level path operations (from LittleFS class)
193
def remove(self, path: str, recursive: bool = False) -> None:
194
"""
195
Remove file or directory with optional recursive deletion.
196
197
Parameters:
198
- path: str, path to remove
199
- recursive: bool, remove directory contents recursively
200
"""
201
202
def rename(self, src: str, dst: str) -> int:
203
"""Rename or move file/directory."""
204
205
def stat(self, path: str) -> LFSStat:
206
"""Get file/directory status information."""
207
208
def unlink(self, path: str) -> int:
209
"""Remove file (alias for remove)."""
210
211
# Low-level path operations (from lfs module)
212
def remove(fs: LFSFilesystem, path: str) -> int:
213
"""Remove file or empty directory."""
214
215
def rename(fs: LFSFilesystem, oldpath: str, newpath: str) -> int:
216
"""Rename or move file/directory."""
217
218
def stat(fs: LFSFilesystem, path: str) -> LFSStat:
219
"""Get file/directory status."""
220
```
221
222
### Extended Attributes
223
224
Metadata storage and retrieval for files and directories.
225
226
```python { .api }
227
# High-level attribute operations (from LittleFS class)
228
def getattr(self, path: str, typ: Union[str, bytes, int]) -> bytes:
229
"""
230
Get extended attribute value.
231
232
Parameters:
233
- path: str, file or directory path
234
- typ: str/bytes/int, attribute type identifier (converted to 0-255 range)
235
236
Returns:
237
bytes: Attribute data
238
"""
239
240
def setattr(self, path: str, typ: Union[str, bytes, int], data: bytes) -> None:
241
"""
242
Set extended attribute value.
243
244
Parameters:
245
- path: str, file or directory path
246
- typ: str/bytes/int, attribute type identifier (converted to 0-255 range)
247
- data: bytes, attribute data to store
248
"""
249
250
def removeattr(self, path: str, typ: Union[str, bytes, int]) -> None:
251
"""
252
Remove extended attribute.
253
254
Parameters:
255
- path: str, file or directory path
256
- typ: str/bytes/int, attribute type identifier (converted to 0-255 range)
257
"""
258
259
# Low-level attribute operations (from lfs module)
260
def getattr(fs: LFSFilesystem, path: str, typ: int) -> bytes:
261
"""Get extended attribute (typ must be 0-255)."""
262
263
def setattr(fs: LFSFilesystem, path: str, typ: int, data: bytes) -> None:
264
"""Set extended attribute (typ must be 0-255)."""
265
266
def removeattr(fs: LFSFilesystem, path: str, typ: int) -> None:
267
"""Remove extended attribute (typ must be 0-255)."""
268
```
269
270
## File Status Information
271
272
```python { .api }
273
class LFSStat(NamedTuple):
274
"""File or directory status information."""
275
type: int # File type: LFS_TYPE_REG (1) or LFS_TYPE_DIR (2)
276
size: int # Size in bytes (0 for directories)
277
name: str # Filename without path
278
279
# Type constants
280
TYPE_REG = 1 # Regular file
281
TYPE_DIR = 2 # Directory
282
```
283
284
## Usage Examples
285
286
### Text File Operations
287
288
```python
289
from littlefs import LittleFS
290
291
fs = LittleFS(block_size=512, block_count=256)
292
293
# Write text file with automatic encoding
294
with fs.open('config.txt', 'w', encoding='utf-8') as f:
295
f.write('Configuration data\\n')
296
f.write('Setting: value\\n')
297
298
# Read text file with line iteration
299
with fs.open('config.txt', 'r', encoding='utf-8') as f:
300
for line_num, line in enumerate(f, 1):
301
print(f"Line {line_num}: {line.rstrip()}")
302
303
# Append to text file
304
with fs.open('config.txt', 'a', encoding='utf-8') as f:
305
f.write('Additional setting: new_value\\n')
306
```
307
308
### Binary File Operations
309
310
```python
311
import struct
312
313
# Write binary data
314
with fs.open('data.bin', 'wb') as f:
315
# Write header
316
f.write(struct.pack('<I', 0x12345678)) # Magic number
317
f.write(struct.pack('<H', 100)) # Record count
318
319
# Write records
320
for i in range(100):
321
f.write(struct.pack('<If', i, i * 1.5))
322
323
# Read binary data with seeking
324
with fs.open('data.bin', 'rb') as f:
325
# Read header
326
magic = struct.unpack('<I', f.read(4))[0]
327
count = struct.unpack('<H', f.read(2))[0]
328
329
print(f"Magic: 0x{magic:08x}, Records: {count}")
330
331
# Seek to specific record
332
record_num = 50
333
f.seek(6 + record_num * 8) # Header size + record size
334
index, value = struct.unpack('<If', f.read(8))
335
print(f"Record {record_num}: index={index}, value={value}")
336
```
337
338
### Directory Tree Operations
339
340
```python
341
# Create complex directory structure
342
fs.makedirs('project/src/main', exist_ok=True)
343
fs.makedirs('project/src/test', exist_ok=True)
344
fs.makedirs('project/docs', exist_ok=True)
345
346
# Create files in structure
347
files_to_create = [
348
'project/README.txt',
349
'project/src/main/app.py',
350
'project/src/main/utils.py',
351
'project/src/test/test_app.py',
352
'project/docs/manual.txt'
353
]
354
355
for filepath in files_to_create:
356
with fs.open(filepath, 'w') as f:
357
f.write(f'Content of {filepath}\\n')
358
359
# Walk entire tree
360
print("Complete directory tree:")
361
for root, dirs, files in fs.walk('project'):
362
level = root.count('/') - 1
363
indent = ' ' * level
364
print(f'{indent}{root}/')
365
366
sub_indent = ' ' * (level + 1)
367
for file in files:
368
stat_info = fs.stat(f"{root}/{file}")
369
print(f'{sub_indent}{file} ({stat_info.size} bytes)')
370
```
371
372
### Low-Level Directory Iteration
373
374
```python
375
from littlefs import lfs
376
377
# Manual directory iteration with full control
378
cfg = lfs.LFSConfig(block_size=512, block_count=256)
379
fs_obj = lfs.LFSFilesystem()
380
lfs.format(fs_obj, cfg)
381
lfs.mount(fs_obj, cfg)
382
383
# Create test files
384
lfs.mkdir(fs_obj, 'test_dir')
385
fh = lfs.file_open(fs_obj, 'test_dir/file1.txt', 'w')
386
lfs.file_write(fs_obj, fh, b'content1')
387
lfs.file_close(fs_obj, fh)
388
389
# Iterate manually
390
dh = lfs.dir_open(fs_obj, 'test_dir')
391
entries = []
392
393
while True:
394
entry = lfs.dir_read(fs_obj, dh)
395
if entry is None:
396
break
397
if entry.name not in ['.', '..']:
398
entries.append(entry)
399
400
lfs.dir_close(fs_obj, dh)
401
402
for entry in entries:
403
type_str = 'file' if entry.type == lfs.LFSStat.TYPE_REG else 'dir'
404
print(f"{entry.name}: {type_str}, {entry.size} bytes")
405
```
406
407
### Extended Attributes Usage
408
409
```python
410
# Store metadata with files
411
fs.mkdir('media')
412
413
# Create media file with metadata
414
with fs.open('media/photo.jpg', 'wb') as f:
415
f.write(b'\\xff\\xd8\\xff\\xe0') # JPEG header
416
417
# Store extended attributes
418
fs.setattr('media/photo.jpg', 'mime-type', b'image/jpeg')
419
fs.setattr('media/photo.jpg', 'camera', b'Canon EOS R5')
420
fs.setattr('media/photo.jpg', 'iso', b'800')
421
fs.setattr('media/photo.jpg', 'created', b'2024-01-15T10:30:00Z')
422
423
# Read metadata
424
mime_type = fs.getattr('media/photo.jpg', 'mime-type').decode()
425
camera = fs.getattr('media/photo.jpg', 'camera').decode()
426
iso = fs.getattr('media/photo.jpg', 'iso').decode()
427
created = fs.getattr('media/photo.jpg', 'created').decode()
428
429
print(f"File: media/photo.jpg")
430
print(f"MIME Type: {mime_type}")
431
print(f"Camera: {camera}")
432
print(f"ISO: {iso}")
433
print(f"Created: {created}")
434
435
# Numeric attribute types
436
fs.setattr('media/photo.jpg', 1, b'width=1920') # Type 1: dimension info
437
fs.setattr('media/photo.jpg', 2, b'height=1080') # Type 2: dimension info
438
439
width_info = fs.getattr('media/photo.jpg', 1).decode()
440
height_info = fs.getattr('media/photo.jpg', 2).decode()
441
print(f"Dimensions: {width_info}, {height_info}")
442
```
443
444
### File Truncation and Sparse Files
445
446
```python
447
# Create file with specific size
448
with fs.open('sparse.dat', 'wb') as f:
449
f.write(b'Header data')
450
# Truncate to create sparse file structure
451
f.truncate(1024) # File now 1024 bytes, rest filled with zeros
452
453
# Verify size and read sparse content
454
stat_info = fs.stat('sparse.dat')
455
print(f"File size: {stat_info.size} bytes")
456
457
with fs.open('sparse.dat', 'rb') as f:
458
header = f.read(11) # Read header
459
f.seek(-10, 2) # Seek to 10 bytes from end
460
tail = f.read() # Read tail (should be zeros)
461
462
print(f"Header: {header}")
463
print(f"Tail: {tail}") # b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'
464
```