CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-jsonargparse

Implement minimal boilerplate CLIs derived from type hints and parse from command line, config files and environment variables.

Pending
Overview
Eval results
Files

signature-arguments.mddocs/

Signature Arguments

Tools for adding arguments to parsers based on function, method, and class signatures. These utilities automatically introspect Python callables and generate appropriate argument configurations using type hints and docstrings, enabling seamless integration between existing code and command-line interfaces.

Capabilities

SignatureArguments Class

Mixin class that provides methods for adding arguments based on callable signatures with automatic type detection and parameter introspection.

class SignatureArguments:
    def add_class_arguments(self, 
        theclass: Type,
        nested_key: Optional[str] = None,
        as_group: bool = True,
        as_positional: bool = False,
        default: Optional[Union[dict, Namespace, Any, Type]] = None,
        skip: Optional[Set[Union[str, int]]] = None,
        instantiate: bool = True,
        fail_untyped: bool = True,
        sub_configs: bool = False,
        **kwargs
    ) -> List[str]:
        """
        Add arguments for a class constructor.
        
        Args:
            theclass: Class to introspect for arguments
            nested_key: Key for nested configuration
            as_group: Whether to group arguments  
            as_positional: Whether to add as positional argument
            default: Default value or configuration
            skip: Parameter names/positions to skip
            instantiate: Whether to instantiate the class
            fail_untyped: Whether to fail on untyped parameters
            sub_configs: Whether to enable sub-configurations
            
        Returns:
            List[str]: List of added argument names
        """
    
    def add_method_arguments(self, 
        theclass: Type,
        themethod: str,
        nested_key: Optional[str] = None,
        as_group: bool = True,
        as_positional: bool = False,
        skip: Optional[Set[Union[str, int]]] = None,
        fail_untyped: bool = True,
        sub_configs: bool = False,
        **kwargs
    ) -> List[str]:
        """
        Add arguments for a class method.
        
        Args:
            theclass: Class containing the method
            themethod: Method name to introspect
            nested_key: Key for nested configuration
            as_group: Whether to group arguments
            as_positional: Whether to add as positional argument
            skip: Parameter names/positions to skip
            fail_untyped: Whether to fail on untyped parameters
            sub_configs: Whether to enable sub-configurations
            
        Returns:
            List[str]: List of added argument names
        """
    
    def add_function_arguments(self,
        function: Callable,
        nested_key: Optional[str] = None,
        as_group: bool = True,
        as_positional: bool = False,
        skip: Optional[Set[Union[str, int]]] = None,
        fail_untyped: bool = True,
        sub_configs: bool = False,
        **kwargs
    ) -> List[str]:
        """
        Add arguments for a function.
        
        Args:
            function: Function to introspect for arguments
            nested_key: Key for nested configuration
            as_group: Whether to group arguments
            as_positional: Whether to add as positional argument
            skip: Parameter names/positions to skip
            fail_untyped: Whether to fail on untyped parameters
            sub_configs: Whether to enable sub-configurations
            
        Returns:
            List[str]: List of added argument names
        """
    
    def add_subclass_arguments(self,
        baseclass: Union[Type, Tuple[Type, ...]],
        nested_key: str,
        as_group: bool = True,
        skip: Optional[Set[str]] = None,
        instantiate: bool = True,
        required: bool = False,
        metavar: str = "CONFIG | CLASS_PATH_OR_NAME | .INIT_ARG_NAME VALUE",
        help: str = "One or more arguments specifying \"class_path\" and \"init_args\"...",
        **kwargs
    ) -> List[str]:
        """
        Add arguments for subclass selection and instantiation.
        
        Args:
            baseclass: Base class or tuple of base classes
            nested_key: Key for nested configuration
            as_group: Whether to group arguments
            skip: Parameter names to skip
            instantiate: Whether to instantiate the selected class
            required: Whether subclass selection is required
            metavar: Metavar for help display
            help: Help text for the subclass argument
            
        Returns:
            List[str]: List of added argument names
        """

Dataclass Composition

Utility for composing multiple dataclasses into a single dataclass for complex configuration scenarios.

def compose_dataclasses(*dataclasses: Type) -> Type:
    """
    Create a dataclass that inherits from multiple dataclasses.
    
    Args:
        *dataclasses: Dataclass types to compose
        
    Returns:
        Type: New dataclass inheriting from all input dataclasses
    """

Usage Examples

Basic Class Arguments

from jsonargparse import ArgumentParser
from dataclasses import dataclass

@dataclass 
class ModelConfig:
    hidden_size: int = 128
    dropout_rate: float = 0.1
    num_layers: int = 3
    activation: str = "relu"

# Create parser and add class arguments
parser = ArgumentParser()
parser.add_class_arguments(ModelConfig, "model")

# Parse arguments
config = parser.parse_args()

# Access nested configuration
print(f"Hidden size: {config.model.hidden_size}")
print(f"Dropout: {config.model.dropout_rate}")

# Instantiate the class if needed
model_instance = ModelConfig(**config.model.as_dict())

Usage:

python script.py --model.hidden_size 256 --model.dropout_rate 0.2 --model.num_layers 5

Function Arguments

from jsonargparse import ArgumentParser

def train_model(
    data_path: str,
    model_name: str,
    epochs: int = 100,
    learning_rate: float = 0.001,
    batch_size: int = 32,
    save_checkpoints: bool = True
) -> None:
    """Train a machine learning model.
    
    Args:
        data_path: Path to training data
        model_name: Name of the model architecture
        epochs: Number of training epochs
        learning_rate: Learning rate for training
        batch_size: Batch size for training
        save_checkpoints: Whether to save model checkpoints
    """
    print(f"Training {model_name} for {epochs} epochs")
    print(f"Data: {data_path}, LR: {learning_rate}, Batch: {batch_size}")

# Add function arguments to parser
parser = ArgumentParser()
parser.add_function_arguments(train_model)

config = parser.parse_args()

# Call function with parsed arguments
train_model(
    data_path=config.data_path,
    model_name=config.model_name,
    epochs=config.epochs,
    learning_rate=config.learning_rate,
    batch_size=config.batch_size,
    save_checkpoints=config.save_checkpoints
)

Method Arguments

from jsonargparse import ArgumentParser

class DataProcessor:
    def __init__(self, base_path: str):
        self.base_path = base_path
    
    def process(self, 
        input_format: str,
        output_format: str = "json",
        validate: bool = True,
        chunk_size: int = 1000
    ) -> None:
        """Process data with specified parameters.
        
        Args:
            input_format: Format of input data
            output_format: Format for output data
            validate: Whether to validate processed data
            chunk_size: Size of processing chunks
        """
        print(f"Processing {input_format} -> {output_format}")
        print(f"Validation: {validate}, Chunk size: {chunk_size}")

# Add method arguments
parser = ArgumentParser()
parser.add_method_arguments(DataProcessor, "process")

config = parser.parse_args()

# Create instance and call method
processor = DataProcessor("/data")
processor.process(
    input_format=config.input_format,
    output_format=config.output_format,
    validate=config.validate,
    chunk_size=config.chunk_size
)

Subclass Arguments

from jsonargparse import ArgumentParser
from abc import ABC, abstractmethod

class Optimizer(ABC):
    @abstractmethod
    def step(self) -> None:
        pass

class SGD(Optimizer):
    def __init__(self, learning_rate: float = 0.01, momentum: float = 0.0):
        self.learning_rate = learning_rate
        self.momentum = momentum
    
    def step(self) -> None:
        print(f"SGD step: lr={self.learning_rate}, momentum={self.momentum}")

class Adam(Optimizer):
    def __init__(self, learning_rate: float = 0.001, beta1: float = 0.9, beta2: float = 0.999):
        self.learning_rate = learning_rate
        self.beta1 = beta1
        self.beta2 = beta2
    
    def step(self) -> None:
        print(f"Adam step: lr={self.learning_rate}, β1={self.beta1}, β2={self.beta2}")

# Add subclass arguments for optimizer selection
parser = ArgumentParser()
parser.add_subclass_arguments(Optimizer, "optimizer", required=True)

config = parser.parse_args()

# The optimizer is automatically instantiated
optimizer = config.optimizer
optimizer.step()

Usage:

# Use SGD optimizer
python script.py --optimizer SGD --optimizer.learning_rate 0.02 --optimizer.momentum 0.9

# Use Adam optimizer  
python script.py --optimizer Adam --optimizer.learning_rate 0.0005 --optimizer.beta1 0.95

Composed Dataclasses

from dataclasses import dataclass
from jsonargparse import ArgumentParser, compose_dataclasses

@dataclass
class ModelConfig:
    hidden_size: int = 128
    num_layers: int = 3

@dataclass  
class TrainingConfig:
    epochs: int = 100
    learning_rate: float = 0.001
    batch_size: int = 32

@dataclass
class LoggingConfig:
    log_level: str = "INFO"
    log_file: str = "training.log"

# Compose all configs into one
AllConfig = compose_dataclasses(ModelConfig, TrainingConfig, LoggingConfig)

parser = ArgumentParser()
parser.add_class_arguments(AllConfig)

config = parser.parse_args()

# Access all composed fields
print(f"Model: {config.hidden_size} hidden, {config.num_layers} layers")
print(f"Training: {config.epochs} epochs, lr={config.learning_rate}")
print(f"Logging: level={config.log_level}, file={config.log_file}")

Nested Configuration

from dataclasses import dataclass
from jsonargparse import ArgumentParser

@dataclass
class DatabaseConfig:
    host: str = "localhost"
    port: int = 5432
    username: str = "user"
    password: str = "pass"

@dataclass
class CacheConfig:
    enabled: bool = True
    ttl: int = 3600
    max_size: int = 1000

@dataclass
class AppConfig:
    debug: bool = False
    workers: int = 4

parser = ArgumentParser()

# Add nested configurations
parser.add_class_arguments(DatabaseConfig, "database", as_group=True)
parser.add_class_arguments(CacheConfig, "cache", as_group=True)  
parser.add_class_arguments(AppConfig, "app", as_group=True)

config = parser.parse_args()

# Access nested configs
print(f"DB: {config.database.host}:{config.database.port}")
print(f"Cache: enabled={config.cache.enabled}, ttl={config.cache.ttl}")
print(f"App: debug={config.app.debug}, workers={config.app.workers}")

Usage:

python script.py --database.host db.example.com --database.port 3306 \
                  --cache.enabled --cache.ttl 7200 \
                  --app.debug --app.workers 8

Skip Parameters

from jsonargparse import ArgumentParser

def process_data(
    input_file: str,
    output_file: str,
    temp_dir: str = "/tmp",  # Skip this parameter
    debug_mode: bool = False,
    log_level: str = "INFO"   # Skip this parameter too
) -> None:
    """Process data files."""
    print(f"Processing {input_file} -> {output_file}")

parser = ArgumentParser()

# Skip temp_dir (index 2) and log_level (by name)
parser.add_function_arguments(
    process_data, 
    skip={"temp_dir", 4}  # Skip by name and position
)

config = parser.parse_args()

# Only input_file, output_file, and debug_mode will be available
process_data(
    input_file=config.input_file,
    output_file=config.output_file,
    debug_mode=config.debug_mode,
    # temp_dir and log_level use their defaults
)

Advanced Features

Configuration Groups

Arguments can be organized into logical groups for better help display:

parser.add_class_arguments(ModelConfig, "model", as_group=True)
parser.add_class_arguments(TrainingConfig, "training", as_group=True)

Default Value Handling

Provide custom default values or configurations:

# Use custom defaults
default_config = {"hidden_size": 256, "dropout_rate": 0.2}
parser.add_class_arguments(ModelConfig, default=default_config)

# Use existing instance as default
existing_config = ModelConfig(hidden_size=512, num_layers=6)
parser.add_class_arguments(ModelConfig, default=existing_config)

Type Validation

Control behavior for untyped parameters:

# Fail on untyped parameters (default)
parser.add_function_arguments(my_function, fail_untyped=True)

# Allow untyped parameters (treated as strings)
parser.add_function_arguments(my_function, fail_untyped=False)

Install with Tessl CLI

npx tessl i tessl/pypi-jsonargparse

docs

advanced-actions.md

cli-creation.md

core-parser.md

index.md

namespace-management.md

settings.md

signature-arguments.md

types-validation.md

utilities.md

tile.json