CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-littlefs-python

A Python wrapper for LittleFS filesystem designed for embedded systems with minimal RAM and flash requirements

Pending
Overview
Eval results
Files

file-operations.mddocs/

File and Directory Operations

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.

Capabilities

FileHandle Class

Raw I/O file handle that extends Python's io.RawIOBase for direct binary file operations with LittleFS-specific optimizations.

class FileHandle(io.RawIOBase):
    def __init__(self, fs: LFSFilesystem, fh: LFSFile):
        """
        Initialize file handle wrapper.
        
        Parameters:
        - fs: LFSFilesystem, filesystem object
        - fh: LFSFile, low-level file handle
        """

def close(self) -> None:
    """
    Close the file handle.
    LittleFS automatically flushes on close.
    """

def readable(self) -> bool:
    """
    Check if file is readable.
    
    Returns:
    bool: True if file was opened for reading
    """

def writable(self) -> bool:
    """
    Check if file is writable.
    
    Returns:
    bool: True if file was opened for writing
    """

def seekable(self) -> bool:
    """
    Check if file supports seeking.
    
    Returns:
    bool: Always True for LittleFS files
    """

def seek(self, offset: int, whence: int = io.SEEK_SET) -> int:
    """
    Seek to position in file.
    
    Parameters:
    - offset: int, byte offset
    - whence: int, seek reference (SEEK_SET, SEEK_CUR, SEEK_END)
    
    Returns:
    int: New absolute position
    """

def tell(self) -> int:
    """
    Get current file position.
    
    Returns:
    int: Current byte position
    """

def truncate(self, size: int = None) -> int:
    """
    Truncate file to specified size.
    
    Parameters:
    - size: int, new file size (default: current position)
    
    Returns:
    int: New file size
    """

def write(self, data: bytes) -> int:
    """
    Write data to file.
    
    Parameters:
    - data: bytes, data to write
    
    Returns:
    int: Number of bytes written
    """

def readinto(self, b: bytearray) -> int:
    """
    Read data into existing buffer.
    
    Parameters:
    - b: bytearray, buffer to read into
    
    Returns:
    int: Number of bytes read
    """

def readall(self) -> bytes:
    """
    Read all remaining data from current position.
    
    Returns:
    bytes: All data from current position to end of file
    """

def flush(self) -> None:
    """
    Flush file buffers to storage.
    """

File I/O Operations

High-level file operations integrated with Python's I/O system.

# File opening with Python semantics (from LittleFS class)
def open(self, fname: str, mode="r", buffering: int = -1, encoding: str = None, 
         errors: str = None, newline: str = None):
    """
    Open file with full Python I/O stack integration.
    
    Mode combinations:
    - 'r': read only (default)
    - 'w': write only, truncate existing
    - 'a': write only, append to end
    - 'x': write only, fail if exists
    - '+': read and write
    - 'b': binary mode
    - 't': text mode (default)
    
    Returns TextIOWrapper, BufferedReader, BufferedWriter, or FileHandle
    based on mode and buffering settings.
    """

Directory Management

Directory creation, listing, and navigation operations.

# High-level directory operations (from LittleFS class)
def listdir(self, path=".") -> List[str]:
    """List directory contents as filename strings."""

def scandir(self, path="."):
    """Scan directory yielding LFSStat objects with full metadata."""

def mkdir(self, path: str) -> int:
    """Create single directory."""

def makedirs(self, name: str, exist_ok=False):
    """Create directory tree recursively."""

def rmdir(self, path: str) -> int:
    """Remove empty directory."""

def removedirs(self, name):
    """Remove directory tree upward while empty."""

def walk(self, top: str):
    """Walk directory tree yielding (root, dirs, files) tuples."""

# Low-level directory operations (from lfs module)
def dir_open(fs: LFSFilesystem, path: str) -> LFSDirectory:
    """Open directory for manual iteration."""

def dir_read(fs: LFSFilesystem, dh: LFSDirectory) -> Optional[LFSStat]:
    """Read next directory entry, None when exhausted."""

def dir_close(fs: LFSFilesystem, dh: LFSDirectory) -> int:
    """Close directory handle."""

def dir_tell(fs: LFSFilesystem, dh: LFSDirectory) -> int:
    """Get current directory iteration position."""

def dir_rewind(fs: LFSFilesystem, dh: LFSDirectory) -> int:
    """Reset directory iteration to beginning."""

Path Operations

File and directory manipulation at the path level.

# High-level path operations (from LittleFS class)
def remove(self, path: str, recursive: bool = False) -> None:
    """
    Remove file or directory with optional recursive deletion.
    
    Parameters:
    - path: str, path to remove
    - recursive: bool, remove directory contents recursively
    """

def rename(self, src: str, dst: str) -> int:
    """Rename or move file/directory."""

def stat(self, path: str) -> LFSStat:
    """Get file/directory status information."""

def unlink(self, path: str) -> int:
    """Remove file (alias for remove)."""

# Low-level path operations (from lfs module)  
def remove(fs: LFSFilesystem, path: str) -> int:
    """Remove file or empty directory."""

def rename(fs: LFSFilesystem, oldpath: str, newpath: str) -> int:
    """Rename or move file/directory."""

def stat(fs: LFSFilesystem, path: str) -> LFSStat:
    """Get file/directory status."""

Extended Attributes

Metadata storage and retrieval for files and directories.

# High-level attribute operations (from LittleFS class)
def getattr(self, path: str, typ: Union[str, bytes, int]) -> bytes:
    """
    Get extended attribute value.
    
    Parameters:
    - path: str, file or directory path
    - typ: str/bytes/int, attribute type identifier (converted to 0-255 range)
    
    Returns:
    bytes: Attribute data
    """

def setattr(self, path: str, typ: Union[str, bytes, int], data: bytes) -> None:
    """
    Set extended attribute value.
    
    Parameters:
    - path: str, file or directory path  
    - typ: str/bytes/int, attribute type identifier (converted to 0-255 range)
    - data: bytes, attribute data to store
    """

def removeattr(self, path: str, typ: Union[str, bytes, int]) -> None:
    """
    Remove extended attribute.
    
    Parameters:
    - path: str, file or directory path
    - typ: str/bytes/int, attribute type identifier (converted to 0-255 range)
    """

# Low-level attribute operations (from lfs module)
def getattr(fs: LFSFilesystem, path: str, typ: int) -> bytes:
    """Get extended attribute (typ must be 0-255)."""

def setattr(fs: LFSFilesystem, path: str, typ: int, data: bytes) -> None:
    """Set extended attribute (typ must be 0-255)."""

def removeattr(fs: LFSFilesystem, path: str, typ: int) -> None:
    """Remove extended attribute (typ must be 0-255)."""

File Status Information

class LFSStat(NamedTuple):
    """File or directory status information."""
    type: int  # File type: LFS_TYPE_REG (1) or LFS_TYPE_DIR (2)
    size: int  # Size in bytes (0 for directories)
    name: str  # Filename without path
    
    # Type constants
    TYPE_REG = 1  # Regular file
    TYPE_DIR = 2  # Directory

Usage Examples

Text File Operations

from littlefs import LittleFS

fs = LittleFS(block_size=512, block_count=256)

# Write text file with automatic encoding
with fs.open('config.txt', 'w', encoding='utf-8') as f:
    f.write('Configuration data\\n')
    f.write('Setting: value\\n')

# Read text file with line iteration
with fs.open('config.txt', 'r', encoding='utf-8') as f:
    for line_num, line in enumerate(f, 1):
        print(f"Line {line_num}: {line.rstrip()}")

# Append to text file
with fs.open('config.txt', 'a', encoding='utf-8') as f:
    f.write('Additional setting: new_value\\n')

Binary File Operations

import struct

# Write binary data
with fs.open('data.bin', 'wb') as f:
    # Write header
    f.write(struct.pack('<I', 0x12345678))  # Magic number
    f.write(struct.pack('<H', 100))          # Record count
    
    # Write records
    for i in range(100):
        f.write(struct.pack('<If', i, i * 1.5))

# Read binary data with seeking
with fs.open('data.bin', 'rb') as f:
    # Read header
    magic = struct.unpack('<I', f.read(4))[0]
    count = struct.unpack('<H', f.read(2))[0]
    
    print(f"Magic: 0x{magic:08x}, Records: {count}")
    
    # Seek to specific record
    record_num = 50
    f.seek(6 + record_num * 8)  # Header size + record size
    index, value = struct.unpack('<If', f.read(8))
    print(f"Record {record_num}: index={index}, value={value}")

Directory Tree Operations

# Create complex directory structure
fs.makedirs('project/src/main', exist_ok=True)
fs.makedirs('project/src/test', exist_ok=True) 
fs.makedirs('project/docs', exist_ok=True)

# Create files in structure
files_to_create = [
    'project/README.txt',
    'project/src/main/app.py',
    'project/src/main/utils.py',
    'project/src/test/test_app.py',
    'project/docs/manual.txt'
]

for filepath in files_to_create:
    with fs.open(filepath, 'w') as f:
        f.write(f'Content of {filepath}\\n')

# Walk entire tree
print("Complete directory tree:")
for root, dirs, files in fs.walk('project'):
    level = root.count('/') - 1
    indent = '  ' * level
    print(f'{indent}{root}/')
    
    sub_indent = '  ' * (level + 1)
    for file in files:
        stat_info = fs.stat(f"{root}/{file}")
        print(f'{sub_indent}{file} ({stat_info.size} bytes)')

Low-Level Directory Iteration

from littlefs import lfs

# Manual directory iteration with full control
cfg = lfs.LFSConfig(block_size=512, block_count=256)
fs_obj = lfs.LFSFilesystem()
lfs.format(fs_obj, cfg)  
lfs.mount(fs_obj, cfg)

# Create test files
lfs.mkdir(fs_obj, 'test_dir')
fh = lfs.file_open(fs_obj, 'test_dir/file1.txt', 'w')
lfs.file_write(fs_obj, fh, b'content1')
lfs.file_close(fs_obj, fh)

# Iterate manually
dh = lfs.dir_open(fs_obj, 'test_dir')
entries = []

while True:
    entry = lfs.dir_read(fs_obj, dh)
    if entry is None:
        break
    if entry.name not in ['.', '..']:
        entries.append(entry)
        
lfs.dir_close(fs_obj, dh)

for entry in entries:
    type_str = 'file' if entry.type == lfs.LFSStat.TYPE_REG else 'dir'
    print(f"{entry.name}: {type_str}, {entry.size} bytes")

Extended Attributes Usage

# Store metadata with files
fs.mkdir('media')

# Create media file with metadata
with fs.open('media/photo.jpg', 'wb') as f:
    f.write(b'\\xff\\xd8\\xff\\xe0')  # JPEG header

# Store extended attributes
fs.setattr('media/photo.jpg', 'mime-type', b'image/jpeg')
fs.setattr('media/photo.jpg', 'camera', b'Canon EOS R5')
fs.setattr('media/photo.jpg', 'iso', b'800')
fs.setattr('media/photo.jpg', 'created', b'2024-01-15T10:30:00Z')

# Read metadata
mime_type = fs.getattr('media/photo.jpg', 'mime-type').decode()
camera = fs.getattr('media/photo.jpg', 'camera').decode()
iso = fs.getattr('media/photo.jpg', 'iso').decode()
created = fs.getattr('media/photo.jpg', 'created').decode()

print(f"File: media/photo.jpg")
print(f"MIME Type: {mime_type}")  
print(f"Camera: {camera}")
print(f"ISO: {iso}")
print(f"Created: {created}")

# Numeric attribute types
fs.setattr('media/photo.jpg', 1, b'width=1920')  # Type 1: dimension info
fs.setattr('media/photo.jpg', 2, b'height=1080') # Type 2: dimension info

width_info = fs.getattr('media/photo.jpg', 1).decode()
height_info = fs.getattr('media/photo.jpg', 2).decode()
print(f"Dimensions: {width_info}, {height_info}")

File Truncation and Sparse Files

# Create file with specific size
with fs.open('sparse.dat', 'wb') as f:
    f.write(b'Header data')
    # Truncate to create sparse file structure
    f.truncate(1024)  # File now 1024 bytes, rest filled with zeros

# Verify size and read sparse content
stat_info = fs.stat('sparse.dat')
print(f"File size: {stat_info.size} bytes")

with fs.open('sparse.dat', 'rb') as f:
    header = f.read(11)  # Read header
    f.seek(-10, 2)       # Seek to 10 bytes from end
    tail = f.read()      # Read tail (should be zeros)
    
print(f"Header: {header}")
print(f"Tail: {tail}")  # b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'

Install with Tessl CLI

npx tessl i tessl/pypi-littlefs-python

docs

cli.md

contexts.md

file-operations.md

filesystem.md

index.md

low-level-api.md

tile.json