CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-cement

Advanced Application Framework for Python with a focus on Command Line Interfaces

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

arguments.mddocs/

Argument Parsing

The argument parsing system provides command-line argument processing built on Python's argparse module. It offers unified argument handling that integrates with configuration management and controller systems.

Capabilities

Argument Handler Interface

Base interface for argument parsing functionality that defines the contract for command-line argument operations.

class ArgumentHandler:
    """
    Argument handler interface for parsing command-line arguments.
    
    Provides methods for adding arguments, parsing command-line input,
    and integrating with the application configuration system.
    """
    
    def add_argument(self, *args: Any, **kwargs: Any) -> None:
        """
        Add a command-line argument.
        
        Args:
            *args: Positional arguments (e.g., '--verbose', '-v')
            **kwargs: Keyword arguments (help, action, type, etc.)
        """
    
    def parse(self, argv: List[str]) -> Any:
        """
        Parse command-line arguments.
        
        Args:
            argv: List of command-line arguments to parse
            
        Returns:
            Parsed arguments object
        """

Usage Examples

Basic Argument Parsing

from cement import App, Controller, ex

class BaseController(Controller):
    class Meta:
        label = 'base'
        arguments = [
            (['--verbose', '-v'], {
                'help': 'verbose output',
                'action': 'store_true'
            }),
            (['--config'], {
                'help': 'configuration file path',
                'dest': 'config_file'
            })
        ]
    
    def _default(self):
        """Default action when no sub-command specified."""
        if self.app.pargs.verbose:
            print('Verbose mode enabled')
        
        if hasattr(self.app.pargs, 'config_file') and self.app.pargs.config_file:
            print(f'Using config: {self.app.pargs.config_file}')

class MyApp(App):
    class Meta:
        label = 'myapp'
        base_controller = 'base'
        handlers = [BaseController]

with MyApp() as app:
    app.run()

# Usage:
# myapp --verbose
# myapp --config /path/to/config.conf

Command-Specific Arguments

from cement import App, Controller, ex

class FileController(Controller):
    class Meta:
        label = 'file'
        stacked_on = 'base'
        stacked_type = 'nested'
    
    @ex(
        help='process files',
        arguments=[
            (['files'], {
                'nargs': '+',
                'help': 'files to process'
            }),
            (['--output', '-o'], {
                'help': 'output directory',
                'default': './output'
            }),
            (['--format'], {
                'choices': ['json', 'yaml', 'xml'],
                'default': 'json',
                'help': 'output format'
            }),
            (['--recursive', '-r'], {
                'action': 'store_true',
                'help': 'process directories recursively'
            })
        ]
    )
    def process(self):
        """Process files with various options."""
        files = self.app.pargs.files
        output_dir = self.app.pargs.output
        format_type = self.app.pargs.format
        recursive = self.app.pargs.recursive
        
        print(f'Processing {len(files)} files')
        print(f'Output directory: {output_dir}')
        print(f'Format: {format_type}')
        print(f'Recursive: {recursive}')
        
        for file_path in files:
            print(f'Processing: {file_path}')

class BaseController(Controller):
    class Meta:
        label = 'base'

class MyApp(App):
    class Meta:
        label = 'myapp'
        base_controller = 'base'
        handlers = [BaseController, FileController]

with MyApp() as app:
    app.run()

# Usage:
# myapp file process file1.txt file2.txt --format yaml --recursive

Global vs Local Arguments

from cement import App, Controller, ex

class BaseController(Controller):
    class Meta:
        label = 'base'
        # Global arguments available to all commands
        arguments = [
            (['--debug'], {
                'action': 'store_true',
                'help': 'enable debug mode'
            }),
            (['--log-level'], {
                'choices': ['DEBUG', 'INFO', 'WARNING', 'ERROR'],
                'default': 'INFO',
                'help': 'set logging level'
            })
        ]
    
    @ex(
        help='show application status',
        # Command-specific arguments
        arguments=[
            (['--detailed'], {
                'action': 'store_true',
                'help': 'show detailed status'
            })
        ]
    )
    def status(self):
        """Show application status."""
        # Access global arguments
        debug = self.app.pargs.debug
        log_level = self.app.pargs.log_level
        
        # Access command-specific arguments
        detailed = self.app.pargs.detailed
        
        print(f'Debug mode: {debug}')
        print(f'Log level: {log_level}')
        
        if detailed:
            print('Detailed status information...')

class MyApp(App):
    class Meta:
        label = 'myapp'
        base_controller = 'base'
        handlers = [BaseController]

with MyApp() as app:
    app.run()

# Usage:
# myapp --debug --log-level DEBUG status --detailed

Argument Validation

from cement import App, Controller, ex
import os

def validate_file_exists(value):
    """Custom argument type that validates file exists."""
    if not os.path.exists(value):
        raise argparse.ArgumentTypeError(f"File '{value}' does not exist")
    return value

def validate_positive_int(value):
    """Custom argument type that validates positive integers."""
    try:
        ivalue = int(value)
        if ivalue <= 0:
            raise argparse.ArgumentTypeError(f"'{value}' must be a positive integer")
        return ivalue
    except ValueError:
        raise argparse.ArgumentTypeError(f"'{value}' is not a valid integer")

class BaseController(Controller):
    class Meta:
        label = 'base'
    
    @ex(
        help='backup file',
        arguments=[
            (['source'], {
                'type': validate_file_exists,
                'help': 'source file to backup'
            }),
            (['--destination'], {
                'help': 'backup destination path'
            }),
            (['--max-backups'], {
                'type': validate_positive_int,
                'default': 5,
                'help': 'maximum number of backups to keep'
            })
        ]
    )
    def backup(self):
        """Backup a file with validation."""
        source = self.app.pargs.source
        destination = self.app.pargs.destination or f"{source}.bak"
        max_backups = self.app.pargs.max_backups
        
        print(f'Backing up {source} to {destination}')
        print(f'Keeping maximum {max_backups} backups')

class MyApp(App):
    class Meta:
        label = 'myapp'
        base_controller = 'base'
        handlers = [BaseController]

with MyApp() as app:
    app.run()

# Usage:
# myapp backup /path/to/file.txt --max-backups 10

Environment Variable Integration

from cement import App, Controller, ex
import os

class BaseController(Controller):
    class Meta:
        label = 'base'
        arguments = [
            (['--database-url'], {
                'help': 'database connection URL',
                'default': os.getenv('DATABASE_URL', 'sqlite:///app.db'),
                'dest': 'database_url'
            }),
            (['--api-key'], {
                'help': 'API key for external service',
                'default': os.getenv('API_KEY'),
                'dest': 'api_key'
            }),
            (['--workers'], {
                'type': int,
                'help': 'number of worker processes',
                'default': int(os.getenv('WORKERS', '4')),
                'dest': 'worker_count'
            })
        ]
    
    @ex(help='show configuration')
    def config(self):
        """Show current configuration from args/env."""
        print(f'Database URL: {self.app.pargs.database_url}')
        print(f'API Key: {"***" if self.app.pargs.api_key else "Not set"}')
        print(f'Workers: {self.app.pargs.worker_count}')

class MyApp(App):
    class Meta:
        label = 'myapp'
        base_controller = 'base'
        handlers = [BaseController]

with MyApp() as app:
    app.run()

# Environment variables take precedence:
# DATABASE_URL=postgresql://... myapp config
# myapp config --database-url mysql://...

Complex Argument Patterns

from cement import App, Controller, ex
import argparse

class DeployController(Controller):
    class Meta:
        label = 'deploy'
        stacked_on = 'base'
        stacked_type = 'nested'
    
    @ex(
        help='deploy application',
        arguments=[
            (['environment'], {
                'choices': ['development', 'staging', 'production'],
                'help': 'deployment environment'
            }),
            (['--version'], {
                'help': 'application version to deploy'
            }),
            (['--config-file'], {
                'action': 'append',
                'help': 'configuration files (can be specified multiple times)'
            }),
            (['--dry-run'], {
                'action': 'store_true',
                'help': 'show what would be deployed without actually deploying'
            }),
            (['--force'], {
                'action': 'store_true',
                'help': 'force deployment even if validation fails'
            }),
            (['--exclude'], {
                'action': 'append',
                'help': 'exclude specific components from deployment'
            }),
            (['--timeout'], {
                'type': int,
                'default': 300,
                'help': 'deployment timeout in seconds'
            })
        ]
    )
    def start(self):
        """Start deployment process."""
        env = self.app.pargs.environment
        version = self.app.pargs.version or 'latest'
        config_files = self.app.pargs.config_file or []
        dry_run = self.app.pargs.dry_run
        force = self.app.pargs.force
        excludes = self.app.pargs.exclude or []
        timeout = self.app.pargs.timeout
        
        print(f'Deploying to {env} environment')
        print(f'Version: {version}')
        print(f'Config files: {config_files}')
        print(f'Dry run: {dry_run}')
        print(f'Force: {force}')
        print(f'Excludes: {excludes}')
        print(f'Timeout: {timeout}s')

class BaseController(Controller):
    class Meta:
        label = 'base'

class MyApp(App):
    class Meta:
        label = 'myapp'
        base_controller = 'base'
        handlers = [BaseController, DeployController]

with MyApp() as app:
    app.run()

# Usage:
# myapp deploy production --version 1.2.3 --config-file prod.conf --config-file secrets.conf --exclude monitoring --dry-run

Install with Tessl CLI

npx tessl i tessl/pypi-cement

docs

arguments.md

caching.md

configuration.md

controllers.md

extensions.md

foundation.md

hooks.md

index.md

interface-handler.md

logging.md

mail.md

output.md

plugins.md

templates.md

utilities.md

tile.json