# Command Line Interface

CLI argument parsing and settings generation with support for subcommands, flags, positional arguments, and running Pydantic models as CLI applications. The CLI system provides comprehensive integration with argparse and supports complex argument structures.

## Capabilities

### CLI Settings Source

Parse command-line arguments and convert them to settings values with full integration with argparse functionality.

```python { .api }
class CliSettingsSource(EnvSettingsSource, Generic[T]):
    """Source class for loading settings values from CLI."""
    
    def __init__(
        self,
        settings_cls: type[BaseSettings],
        cli_prog_name: str | None = None,
        cli_parse_args: bool | list[str] | tuple[str, ...] | None = None,
        cli_parse_none_str: str | None = None,
        cli_hide_none_type: bool = False,
        cli_avoid_json: bool = False,
        cli_enforce_required: bool = False,
        cli_use_class_docs_for_groups: bool = False,
        cli_exit_on_error: bool = True,
        cli_prefix: str = "",
        cli_flag_prefix_char: str = "-",
        cli_implicit_flags: bool | None = None,
        cli_ignore_unknown_args: bool | None = None,
        cli_kebab_case: bool | None = None,
        cli_shortcuts: Mapping[str, str | list[str]] | None = None,
        case_sensitive: bool | None = None,
    ):
        """
        Initialize CLI settings source.
        
        Parameters:
        - settings_cls: The settings class
        - cli_prog_name: Program name for help text
        - cli_parse_args: Arguments to parse (True for sys.argv[1:])
        - cli_parse_none_str: String value to parse as None
        - cli_hide_none_type: Hide None values in help text
        - cli_avoid_json: Avoid complex JSON objects in help
        - cli_enforce_required: Enforce required fields at CLI
        - cli_use_class_docs_for_groups: Use class docs for group help
        - cli_exit_on_error: Exit on parsing errors
        - cli_prefix: Root parser arguments prefix
        - cli_flag_prefix_char: Flag prefix character
        - cli_implicit_flags: Convert bool fields to flags
        - cli_ignore_unknown_args: Ignore unknown CLI args
        - cli_kebab_case: Use kebab-case for CLI args
        - cli_shortcuts: Mapping of field names to alias names
        - case_sensitive: Whether CLI args are case-sensitive
        """
```

### CLI Application Runner

Utility class for running Pydantic models as CLI applications with support for subcommands and async methods.

```python { .api }
class CliApp:
    """Utility class for running Pydantic models as CLI applications."""
    
    @staticmethod
    def run(
        model_cls: type[T],
        cli_args: list[str] | Namespace | SimpleNamespace | dict[str, Any] | None = None,
        cli_settings_source: CliSettingsSource[Any] | None = None,
        cli_exit_on_error: bool | None = None,
        cli_cmd_method_name: str = 'cli_cmd',
        **model_init_data: Any,
    ) -> T:
        """
        Run a Pydantic model as a CLI application.
        
        Parameters:
        - model_cls: The model class to run
        - cli_args: CLI arguments to parse (defaults to sys.argv[1:])
        - cli_settings_source: Custom CLI settings source
        - cli_exit_on_error: Whether to exit on error
        - cli_cmd_method_name: CLI command method name to run
        - **model_init_data: Additional model initialization data
        
        Returns:
        The model instance after running the CLI command
        
        Raises:
        - SettingsError: If model_cls is not a BaseModel or dataclass
        - SettingsError: If model_cls lacks the required CLI command method
        """
    
    @staticmethod
    def run_subcommand(
        model: PydanticModel,
        cli_exit_on_error: bool | None = None,
        cli_cmd_method_name: str = 'cli_cmd'
    ) -> PydanticModel:
        """
        Run a model subcommand.
        
        Parameters:
        - model: The model to run the subcommand from
        - cli_exit_on_error: Whether to exit on error
        - cli_cmd_method_name: CLI command method name to run
        
        Returns:
        The subcommand model after execution
        
        Raises:
        - SystemExit: When no subcommand found and cli_exit_on_error=True
        - SettingsError: When no subcommand found and cli_exit_on_error=False
        """
```

### CLI Annotations

Type annotations for defining CLI-specific field behavior including subcommands, positional arguments, and flags.

```python { .api }
# CLI subcommand annotation
CliSubCommand = Annotated[Union[T, None], _CliSubCommand]

# Positional argument annotation
CliPositionalArg = Annotated[T, _CliPositionalArg]

# Boolean flag annotations
CliImplicitFlag = Annotated[bool, _CliImplicitFlag]
CliExplicitFlag = Annotated[bool, _CliExplicitFlag]

# Suppress CLI argument generation
CliSuppress = Annotated[T, CLI_SUPPRESS]

# Capture unknown CLI arguments
CliUnknownArgs = Annotated[list[str], Field(default=[]), _CliUnknownArgs, NoDecode]
```

### CLI Group Management

Model for managing mutually exclusive CLI argument groups.

```python { .api }
class CliMutuallyExclusiveGroup(BaseModel):
    """Model for mutually exclusive CLI argument groups."""
    pass
```

### Constants

```python { .api }
# Suppress argument generation (from argparse.SUPPRESS)
CLI_SUPPRESS: str
```

## Usage Examples

### Basic CLI Integration

```python
from pydantic_settings import BaseSettings
from pydantic import Field

class AppSettings(BaseSettings):
    name: str = Field(..., description="Application name")
    debug: bool = Field(False, description="Enable debug mode")
    port: int = Field(8000, description="Server port")
    
    model_config = SettingsConfigDict(
        cli_parse_args=True,
        cli_prog_name="myapp"
    )

# Command line: python app.py --name "My App" --debug --port 3000
settings = AppSettings()
print(f"Running {settings.name} on port {settings.port}, debug={settings.debug}")
```

### CLI with Custom Prefixes and Kebab Case

```python
class ServerSettings(BaseSettings):
    server_host: str = Field("localhost", description="Server hostname")
    server_port: int = Field(8000, description="Server port")
    enable_ssl: bool = Field(False, description="Enable SSL")
    
    model_config = SettingsConfigDict(
        cli_parse_args=True,
        cli_prefix="server-",
        cli_kebab_case=True,
        cli_implicit_flags=True
    )

# Command line: python app.py --server-server-host prod.example.com --server-enable-ssl
settings = ServerSettings()
```

### CLI Application with Command Method

```python
from pydantic_settings import BaseSettings, CliApp

class CalculatorSettings(BaseSettings):
    operation: str = Field(..., description="Math operation to perform")
    x: float = Field(..., description="First number")
    y: float = Field(..., description="Second number")
    
    def cli_cmd(self):
        """Execute the calculator command."""
        if self.operation == "add":
            result = self.x + self.y
        elif self.operation == "multiply":
            result = self.x * self.y
        else:
            raise ValueError(f"Unknown operation: {self.operation}")
        
        print(f"Result: {result}")

# Run as CLI app
if __name__ == "__main__":
    # Command line: python calculator.py --operation add --x 10 --y 5
    CliApp.run(CalculatorSettings)
```

### CLI with Subcommands

```python
from typing import Union
from pydantic import BaseModel

class DatabaseCommand(BaseModel):
    host: str = Field("localhost", description="Database host")
    
    def cli_cmd(self):
        print(f"Connecting to database at {self.host}")

class ServerCommand(BaseModel):
    port: int = Field(8000, description="Server port")
    
    def cli_cmd(self):
        print(f"Starting server on port {self.port}")

class AppSettings(BaseSettings):
    verbose: bool = Field(False, description="Verbose output")
    command: CliSubCommand[Union[DatabaseCommand, ServerCommand]] = None
    
    model_config = SettingsConfigDict(cli_parse_args=True)

# Command line: python app.py --verbose database --host prod-db.com
# Command line: python app.py server --port 3000
settings = AppSettings()
if settings.verbose:
    print("Verbose mode enabled")

if settings.command:
    CliApp.run_subcommand(settings)
```

### CLI with Positional Arguments

```python
class FileProcessor(BaseSettings):
    input_file: CliPositionalArg[str] = Field(..., description="Input file path")
    output_file: CliPositionalArg[str] = Field(..., description="Output file path")
    format: str = Field("json", description="Output format")
    verbose: CliImplicitFlag[bool] = Field(False, description="Verbose output")
    
    model_config = SettingsConfigDict(cli_parse_args=True)
    
    def cli_cmd(self):
        print(f"Processing {self.input_file} -> {self.output_file} ({self.format})")

# Command line: python processor.py input.txt output.txt --format csv --verbose
CliApp.run(FileProcessor)
```

### CLI with Explicit Flags and Shortcuts

```python
class BuildSettings(BaseSettings):
    clean: CliExplicitFlag[bool] = Field(False, description="Clean before build")
    release: CliImplicitFlag[bool] = Field(False, description="Release build")
    jobs: int = Field(1, description="Number of parallel jobs")
    
    model_config = SettingsConfigDict(
        cli_parse_args=True,
        cli_shortcuts={
            "jobs": ["j"],
            "clean": ["c"],
            "release": ["r"]
        }
    )

# Command line: python build.py --clean --release -j 4
# Command line: python build.py -c -r -j 8
settings = BuildSettings()
```

### CLI with Unknown Arguments Capture

```python
class FlexibleApp(BaseSettings):
    name: str = Field(..., description="App name")
    debug: bool = Field(False, description="Debug mode") 
    extra_args: CliUnknownArgs = Field(default=[])
    
    model_config = SettingsConfigDict(
        cli_parse_args=True,
        cli_ignore_unknown_args=True
    )
    
    def cli_cmd(self):
        print(f"App: {self.name}, Debug: {self.debug}")
        if self.extra_args:
            print(f"Extra arguments: {self.extra_args}")

# Command line: python app.py --name MyApp --debug --custom-flag value --another-arg
# extra_args will contain: ['--custom-flag', 'value', '--another-arg']
CliApp.run(FlexibleApp)
```

### Async CLI Commands

```python
import asyncio

class AsyncApp(BaseSettings):
    delay: int = Field(1, description="Delay in seconds")
    message: str = Field("Hello", description="Message to display")
    
    async def cli_cmd(self):
        """Async CLI command method."""
        await asyncio.sleep(self.delay)
        print(f"Async message: {self.message}")

# CliApp.run automatically handles async methods
CliApp.run(AsyncApp)
```

### CLI with Custom Help and Error Handling

```python
class RobustApp(BaseSettings):
    """
    A robust application with custom CLI handling.
    
    This app demonstrates error handling and help customization.
    """
    config_file: str = Field(..., description="Configuration file path")
    strict: bool = Field(False, description="Strict validation mode")
    
    model_config = SettingsConfigDict(
        cli_parse_args=True,
        cli_prog_name="robust-app",
        cli_exit_on_error=False,  # Handle errors manually
        cli_use_class_docs_for_groups=True
    )
    
    def cli_cmd(self):
        try:
            with open(self.config_file) as f:
                config = f.read()
            print(f"Loaded config: {len(config)} bytes")
        except FileNotFoundError:
            if self.strict:
                raise
            print(f"Config file {self.config_file} not found, using defaults")

# Will handle parsing errors gracefully
try:
    CliApp.run(RobustApp)
except SettingsError as e:
    print(f"CLI Error: {e}")
```