CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-meshio

I/O for many mesh formats

Pending
Overview
Eval results
Files

format-registration.mddocs/

Format Registration System

System for registering custom file formats and managing format detection based on file extensions, enabling extensibility of meshio with user-defined formats.

Capabilities

Format Registration Functions

Core functions for managing the format registry that controls which readers and writers are available for different file extensions.

def register_format(format_name, extensions, reader, writer_map):
    """
    Register a new file format with meshio's format detection system.
    
    Parameters:
    - format_name: str - Unique identifier for the format
        Used internally to reference the format and in error messages
        Should be descriptive and unique (e.g., "custom_fem", "my_format")
    - extensions: List[str] - List of file extensions associated with this format
        Extensions should include the dot (e.g., [".myfmt", ".dat"])
        Multiple extensions can map to the same format
        Extensions are case-insensitive during detection
    - reader: Callable | None - Function to read files of this format
        Signature: reader(filename) -> Mesh
        Can be None if format is write-only
        Function should handle both file paths and file-like objects
    - writer_map: Dict[str, Callable] - Dictionary mapping format names to writer functions
        Key: format name (usually same as format_name parameter)
        Value: writer function with signature writer(filename, mesh, **kwargs)
        Can be empty dict {} if format is read-only
        
    Side Effects:
    - Updates extension_to_filetypes mapping for auto-detection
    - Registers reader function in global reader_map
    - Registers writer functions in global writer_map
    
    Examples:
    >>> def my_reader(filename):
    ...     # Custom reading logic
    ...     return meshio.Mesh(points, cells)
    >>> 
    >>> def my_writer(filename, mesh, **kwargs):
    ...     # Custom writing logic
    ...     pass
    >>> 
    >>> meshio.register_format(
    ...     "my_format",
    ...     [".myfmt", ".dat"], 
    ...     my_reader,
    ...     {"my_format": my_writer}
    ... )
    """

def deregister_format(format_name):
    """
    Remove a previously registered file format from meshio.
    
    Parameters:
    - format_name: str - Name of format to remove
        Must match the format_name used in register_format()
        
    Side Effects:
    - Removes format from extension_to_filetypes mapping
    - Removes reader from reader_map if present
    - Removes writer from writer_map if present
    - After deregistration, files with associated extensions will no longer be auto-detected
    
    Examples:
    >>> meshio.deregister_format("my_format")
    >>> # Now .myfmt files will not be recognized
    """

Format Detection System

Internal mappings and utilities used by meshio's automatic format detection.

extension_to_filetypes: Dict[str, List[str]]
"""
Dictionary mapping file extensions to lists of format names.

Structure:
- Keys: File extensions including dot (e.g., ".vtk", ".msh", ".stl")
- Values: List of format names that handle this extension
- Multiple formats can handle the same extension
- Extensions are stored in lowercase for case-insensitive matching

Examples:
>>> print(meshio.extension_to_filetypes[".vtk"])
['vtk']
>>> print(meshio.extension_to_filetypes[".msh"]) 
['gmsh']

Note: This is primarily for internal use and format introspection.
Direct modification is not recommended - use register_format() instead.
"""

# Internal mappings (not directly exposed but used by format system)
reader_map: Dict[str, Callable]
"""Internal mapping from format names to reader functions."""

_writer_map: Dict[str, Callable] 
"""Internal mapping from format names to writer functions."""

Usage Examples

Registering a Custom Format

import meshio
import numpy as np

def read_simple_format(filename):
    """
    Reader for a hypothetical simple format.
    
    File format:
    Line 1: number_of_points number_of_triangles
    Next lines: point coordinates (x, y, z)
    Final lines: triangle indices (i, j, k)
    """
    with open(filename, 'r') as f:
        # Read header
        n_points, n_triangles = map(int, f.readline().split())
        
        # Read points
        points = []
        for _ in range(n_points):
            x, y, z = map(float, f.readline().split())
            points.append([x, y, z])
        points = np.array(points)
        
        # Read triangles
        triangles = []
        for _ in range(n_triangles):
            i, j, k = map(int, f.readline().split())
            triangles.append([i, j, k])
        triangles = np.array(triangles)
        
        # Create mesh
        cells = [("triangle", triangles)]
        return meshio.Mesh(points, cells)

def write_simple_format(filename, mesh, **kwargs):
    """
    Writer for the simple format.
    """
    # Extract triangles from mesh
    triangles = mesh.get_cells_type("triangle")
    
    with open(filename, 'w') as f:
        # Write header
        f.write(f"{len(mesh.points)} {len(triangles)}\n")
        
        # Write points
        for point in mesh.points:
            f.write(f"{point[0]} {point[1]} {point[2]}\n")
            
        # Write triangles
        for triangle in triangles:
            f.write(f"{triangle[0]} {triangle[1]} {triangle[2]}\n")

# Register the custom format
meshio.register_format(
    "simple_triangle",           # Format name
    [".tri", ".simple"],         # File extensions
    read_simple_format,          # Reader function
    {"simple_triangle": write_simple_format}  # Writer function
)

# Now can use the format automatically
mesh = meshio.read("model.tri")        # Auto-detects simple_triangle format
meshio.write("output.simple", mesh)    # Auto-detects and uses custom writer

Read-Only Format Registration

def read_special_format(filename):
    """Reader for a read-only format."""
    # ... reading logic ...
    return meshio.Mesh(points, cells)

# Register read-only format
meshio.register_format(
    "read_only_format",
    [".readonly", ".ro"],
    read_special_format,
    {}  # Empty writer map - format is read-only
)

# Usage
mesh = meshio.read("data.readonly")  # Works
# meshio.write("output.readonly", mesh)  # Would raise WriteError

Write-Only Format Registration

def write_visualization_format(filename, mesh, **kwargs):
    """Writer for a visualization-only format."""
    # ... writing logic for visualization ...
    pass

# Register write-only format  
meshio.register_format(
    "viz_format",
    [".viz", ".display"],
    None,  # No reader function
    {"viz_format": write_visualization_format}
)

# Usage
# mesh = meshio.read("file.viz")  # Would raise ReadError
meshio.write("visualization.viz", mesh)  # Works

Multiple Extension Handling

def read_multi_ext_format(filename):
    """Reader that handles multiple extensions with slight variations."""
    # Different behavior based on extension
    if filename.endswith('.v1'):
        # Handle version 1 format
        pass
    elif filename.endswith('.v2'):
        # Handle version 2 format  
        pass
    return meshio.Mesh(points, cells)

def write_multi_ext_format(filename, mesh, **kwargs):
    """Writer that adapts to extension."""
    if filename.endswith('.v1'):
        # Write version 1 format
        pass
    elif filename.endswith('.v2'):
        # Write version 2 format
        pass

# Register format with multiple extensions
meshio.register_format(
    "versioned_format",
    [".v1", ".v2", ".legacy"],
    read_multi_ext_format,
    {"versioned_format": write_multi_ext_format}
)

Format Introspection

# Check what formats are available
print("Registered formats:")
for ext, formats in meshio.extension_to_filetypes.items():
    print(f"  {ext}: {formats}")

# Check if specific format is registered
def is_format_registered(format_name):
    """Check if a format is currently registered."""
    from meshio._helpers import reader_map, _writer_map
    return (format_name in reader_map or 
            format_name in _writer_map)

print("Is 'vtk' registered?", is_format_registered("vtk"))
print("Is 'my_format' registered?", is_format_registered("my_format"))

# Find formats for extension
def get_formats_for_extension(extension):
    """Get all formats that handle a given extension."""
    return meshio.extension_to_filetypes.get(extension.lower(), [])

print("Formats for .msh:", get_formats_for_extension(".msh"))
print("Formats for .vtk:", get_formats_for_extension(".vtk"))

Dynamic Format Loading

import importlib

def register_plugin_format(plugin_module_name):
    """
    Dynamically load and register a format from a plugin module.
    
    The plugin module should define:
    - FORMAT_NAME: str
    - EXTENSIONS: List[str] 
    - read_function: Callable
    - write_function: Callable (optional)
    """
    try:
        plugin = importlib.import_module(plugin_module_name)
        
        # Check required attributes
        if not hasattr(plugin, 'FORMAT_NAME'):
            raise ValueError("Plugin must define FORMAT_NAME")
        if not hasattr(plugin, 'EXTENSIONS'):
            raise ValueError("Plugin must define EXTENSIONS")
        if not hasattr(plugin, 'read_function'):
            print(f"Warning: {plugin_module_name} has no read_function")
            reader = None
        else:
            reader = plugin.read_function
            
        # Optional writer
        writer_map = {}
        if hasattr(plugin, 'write_function'):
            writer_map[plugin.FORMAT_NAME] = plugin.write_function
            
        # Register the format
        meshio.register_format(
            plugin.FORMAT_NAME,
            plugin.EXTENSIONS, 
            reader,
            writer_map
        )
        print(f"Successfully registered format '{plugin.FORMAT_NAME}'")
        
    except ImportError:
        print(f"Could not import plugin module '{plugin_module_name}'")
    except Exception as e:
        print(f"Error registering plugin: {e}")

# Usage
# register_plugin_format("my_mesh_plugin")

Temporary Format Registration

from contextlib import contextmanager

@contextmanager
def temporary_format(format_name, extensions, reader, writer_map):
    """
    Context manager for temporary format registration.
    
    Useful for testing or temporary format overrides.
    """
    # Store original state
    original_extensions = {}
    for ext in extensions:
        if ext in meshio.extension_to_filetypes:
            original_extensions[ext] = meshio.extension_to_filetypes[ext].copy()
    
    try:
        # Register temporary format
        meshio.register_format(format_name, extensions, reader, writer_map)
        yield
    finally:
        # Restore original state
        meshio.deregister_format(format_name)
        
        # Restore original extension mappings
        for ext, original_formats in original_extensions.items():
            meshio.extension_to_filetypes[ext] = original_formats

# Usage
def temp_reader(filename):
    return meshio.Mesh(np.array([[0, 0, 0]]), [])

with temporary_format("temp_fmt", [".tmp"], temp_reader, {}):
    # Format is available only within this block
    mesh = meshio.read("test.tmp")  # Uses temporary reader
    
# Outside the block, .tmp files are no longer recognized

Format Validation

def validate_custom_format(reader_func, writer_func=None):
    """
    Validate that custom format functions have correct signatures.
    """
    import inspect
    
    # Validate reader signature
    if reader_func is not None:
        sig = inspect.signature(reader_func)
        if len(sig.parameters) < 1:
            raise ValueError("Reader function must accept at least one parameter (filename)")
            
    # Validate writer signature  
    if writer_func is not None:
        sig = inspect.signature(writer_func)
        if len(sig.parameters) < 2:
            raise ValueError("Writer function must accept at least two parameters (filename, mesh)")
            
    return True

# Example validation before registration
def my_reader(filename):
    return meshio.Mesh(np.array([[0, 0, 0]]), [])

def my_writer(filename, mesh, **kwargs):
    pass

# Validate before registering
if validate_custom_format(my_reader, my_writer):
    meshio.register_format("validated_format", [".val"], my_reader, {"validated_format": my_writer})

Error Handling

Common Registration Errors

# Handle registration errors gracefully
try:
    meshio.register_format(
        "problematic_format",
        [".bad"],
        lambda x: None,  # Bad reader - doesn't return Mesh
        {"problematic_format": lambda x, y: None}
    )
except Exception as e:
    print(f"Registration failed: {e}")

# Check for conflicts
def register_format_safely(format_name, extensions, reader, writer_map):
    """Register format with conflict detection."""
    # Check for extension conflicts
    conflicts = []
    for ext in extensions:
        if ext in meshio.extension_to_filetypes:
            existing = meshio.extension_to_filetypes[ext]
            conflicts.extend(existing)
    
    if conflicts:
        print(f"Warning: Extensions {extensions} conflict with formats: {conflicts}")
        response = input("Continue? (y/n): ")
        if response.lower() != 'y':
            return False
    
    # Register format
    meshio.register_format(format_name, extensions, reader, writer_map)
    return True

Best Practices

Format Design Guidelines

  1. Reader Functions: Should be robust and handle various edge cases
  2. Writer Functions: Should preserve as much mesh data as possible
  3. Extension Choice: Use descriptive, unique extensions
  4. Error Messages: Provide clear error messages for invalid files
  5. Documentation: Document format specifications and limitations

Performance Considerations

# Efficient readers for large files
def efficient_reader(filename):
    """Reader optimized for large files."""
    # Use memory mapping for large files
    if os.path.getsize(filename) > 100_000_000:  # 100MB
        # Use memory-mapped reading
        pass
    else:
        # Standard reading
        pass
    return mesh

# Lazy loading for complex formats
def lazy_reader(filename):
    """Reader that supports lazy loading of data."""
    # Read only metadata first, load data on demand
    return mesh

The format registration system provides a flexible way to extend meshio with custom file formats while maintaining compatibility with the existing I/O system and automatic format detection.

Install with Tessl CLI

npx tessl i tessl/pypi-meshio

docs

cli.md

core-io.md

format-registration.md

formats.md

index.md

mesh-data.md

tile.json