CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-cyclopts

Intuitive, easy CLIs based on type hints.

Pending
Overview
Eval results
Files

types-validation.mddocs/

Type System and Validation

Comprehensive type conversion system with pre-built validated types and custom validation support for robust CLI input handling.

Capabilities

Core Conversion Function

Central function for converting command-line tokens to Python types.

def convert(type_: type, tokens: list[Token]) -> Any:
    """
    Convert command-line tokens to target Python type.
    
    Parameters
    ----------
    type_
        Target Python type for conversion
    tokens
        List of Token objects containing user input
        
    Returns
    -------
    Any
        Converted value of the target type
        
    Raises
    ------
    CoercionError
        If conversion fails or tokens are invalid for the type
    """

Environment Variable Utilities

Parse environment variables into appropriate types.

def env_var_split(value: str, type_: type) -> list[str]:
    """
    Split environment variable value based on target type.
    
    Parameters
    ----------
    value
        Environment variable string value
    type_
        Target type that determines splitting behavior
        
    Returns
    -------
    list[str]
        List of string tokens for conversion
    """

Pre-built Path Types

Validated Path types with existence and type checking.

# Basic path types
ExistingPath = Annotated[Path, ...]
"""Path that must exist."""

NonExistentPath = Annotated[Path, ...]  
"""Path that must not exist."""

ExistingFile = Annotated[Path, ...]
"""File path that must exist."""

NonExistentFile = Annotated[Path, ...]
"""File path that must not exist."""

ExistingDirectory = Annotated[Path, ...]
"""Directory path that must exist."""

NonExistentDirectory = Annotated[Path, ...]
"""Directory path that must not exist."""

File = Annotated[Path, ...]
"""Generic file path type."""

Directory = Annotated[Path, ...]
"""Generic directory path type."""

# Resolved path types  
ResolvedPath = Annotated[Path, ...]
"""Path resolved to absolute form."""

ResolvedExistingPath = Annotated[Path, ...]
"""Existing path resolved to absolute form."""

ResolvedExistingFile = Annotated[Path, ...]
"""Existing file path resolved to absolute form."""

ResolvedExistingDirectory = Annotated[Path, ...]
"""Existing directory path resolved to absolute form."""

ResolvedDirectory = Annotated[Path, ...]
"""Directory path resolved to absolute form."""

ResolvedFile = Annotated[Path, ...]
"""File path resolved to absolute form."""

# Extension-specific path types
BinPath = Annotated[Path, ...]
"""Binary file path."""

CsvPath = Annotated[Path, ...]
"""CSV file path."""

ImagePath = Annotated[Path, ...]
"""Image file path."""

JsonPath = Annotated[Path, ...]
"""JSON file path."""

Mp4Path = Annotated[Path, ...]
"""MP4 video file path."""

TomlPath = Annotated[Path, ...]
"""TOML configuration file path."""

TxtPath = Annotated[Path, ...]
"""Text file path."""

YamlPath = Annotated[Path, ...]
"""YAML configuration file path."""

# Extension-specific path types with existence constraints
ExistingBinPath = Annotated[Path, ...]
"""Binary file path that must exist."""

NonExistentBinPath = Annotated[Path, ...]
"""Binary file path that must not exist."""

ExistingCsvPath = Annotated[Path, ...]
"""CSV file path that must exist."""

NonExistentCsvPath = Annotated[Path, ...]
"""CSV file path that must not exist."""

ExistingImagePath = Annotated[Path, ...]
"""Image file path that must exist."""

NonExistentImagePath = Annotated[Path, ...]
"""Image file path that must not exist."""

ExistingJsonPath = Annotated[Path, ...]
"""JSON file path that must exist."""

NonExistentJsonPath = Annotated[Path, ...]
"""JSON file path that must not exist."""

ExistingMp4Path = Annotated[Path, ...]
"""MP4 file path that must exist."""

NonExistentMp4Path = Annotated[Path, ...]
"""MP4 file path that must not exist."""

ExistingTomlPath = Annotated[Path, ...]
"""TOML file path that must exist."""

NonExistentTomlPath = Annotated[Path, ...]
"""TOML file path that must not exist."""

ExistingTxtPath = Annotated[Path, ...]
"""Text file path that must exist."""

NonExistentTxtPath = Annotated[Path, ...]
"""Text file path that must not exist."""

ExistingYamlPath = Annotated[Path, ...]
"""YAML file path that must exist."""

NonExistentYamlPath = Annotated[Path, ...]
"""YAML file path that must not exist."""

Pre-built Numeric Types

Validated numeric types with range constraints.

# Float range types
PositiveFloat = Annotated[float, ...]
"""Float greater than 0."""

NonNegativeFloat = Annotated[float, ...]
"""Float greater than or equal to 0."""

NegativeFloat = Annotated[float, ...]
"""Float less than 0."""

NonPositiveFloat = Annotated[float, ...]
"""Float less than or equal to 0."""

# Integer range types
PositiveInt = Annotated[int, ...]
"""Integer greater than 0."""

NonNegativeInt = Annotated[int, ...]
"""Integer greater than or equal to 0."""

NegativeInt = Annotated[int, ...]
"""Integer less than 0."""

NonPositiveInt = Annotated[int, ...]
"""Integer less than or equal to 0."""

# Fixed-width integer types
UInt8 = Annotated[int, ...]
"""8-bit unsigned integer (0-255)."""

Int8 = Annotated[int, ...]
"""8-bit signed integer (-128 to 127)."""

UInt16 = Annotated[int, ...]
"""16-bit unsigned integer (0-65535)."""

Int16 = Annotated[int, ...]
"""16-bit signed integer (-32768 to 32767)."""

UInt32 = Annotated[int, ...]
"""32-bit unsigned integer."""

Int32 = Annotated[int, ...]
"""32-bit signed integer."""

UInt64 = Annotated[int, ...]
"""64-bit unsigned integer."""

Int64 = Annotated[int, ...]
"""64-bit signed integer."""

# Hexadecimal integer types
HexUInt = Annotated[int, ...]
"""Unsigned integer from hexadecimal string."""

HexUInt8 = Annotated[int, ...]
"""8-bit unsigned integer from hexadecimal string."""

HexUInt16 = Annotated[int, ...]
"""16-bit unsigned integer from hexadecimal string."""

HexUInt32 = Annotated[int, ...]
"""32-bit unsigned integer from hexadecimal string."""

HexUInt64 = Annotated[int, ...]
"""64-bit unsigned integer from hexadecimal string."""

Other Validated Types

Additional pre-built types for common use cases.

Json = Annotated[Any, ...]
"""JSON string parsed to Python object."""

Email = Annotated[str, ...]
"""Email address with validation."""

Port = Annotated[int, ...]
"""Network port number (1-65535)."""

URL = Annotated[str, ...]
"""URL with validation."""

Validator Classes

Built-in validators for custom validation logic.

class Number:
    def __init__(
        self,
        min: float | None = None,
        max: float | None = None,
        min_inclusive: bool = True,
        max_inclusive: bool = True
    ):
        """
        Numeric range validator.
        
        Parameters
        ----------
        min
            Minimum allowed value
        max
            Maximum allowed value
        min_inclusive
            Whether minimum is inclusive
        max_inclusive
            Whether maximum is inclusive
        """
        
    def __call__(self, value: float) -> float:
        """Validate numeric value against range constraints."""

class Path:
    def __init__(
        self,
        exists: bool | None = None,
        file_okay: bool = True,
        dir_okay: bool = True,
        resolve: bool = False
    ):
        """
        Path validator with existence and type checking.
        
        Parameters
        ----------
        exists
            Whether path must exist (True), not exist (False), or either (None)
        file_okay
            Whether files are allowed
        dir_okay
            Whether directories are allowed
        resolve
            Whether to resolve path to absolute form
        """
        
    def __call__(self, value: Path) -> Path:
        """Validate path against constraints."""

class LimitedChoice:
    def __init__(self, *choices: Any, case_sensitive: bool = True):
        """
        Limit value to specific choices.
        
        Parameters
        ----------
        choices
            Valid choice values
        case_sensitive
            Whether string comparison is case sensitive
        """
        
    def __call__(self, value: Any) -> Any:
        """Validate value is in allowed choices."""

class MutuallyExclusive:
    def __init__(self, *groups: str):
        """
        Ensure mutual exclusivity between parameter groups.
        
        Parameters
        ----------
        groups
            Parameter group names that are mutually exclusive
        """
        
    def __call__(self, namespace: dict) -> dict:
        """Validate mutual exclusivity constraints."""

Validator Functions

Standalone validator functions for common patterns.

def mutually_exclusive(*groups: str) -> Callable:
    """
    Create validator for mutually exclusive parameter groups.
    
    Parameters
    ----------
    groups
        Parameter group names that are mutually exclusive
        
    Returns
    -------
    Callable
        Validator function
    """

def all_or_none(*parameters: str) -> Callable:
    """
    Create validator requiring all or none of the specified parameters.
    
    Parameters
    ----------
    parameters
        Parameter names with all-or-none constraint
        
    Returns
    -------
    Callable
        Validator function
    """

Usage Examples

Custom Type with Validation

from cyclopts import App, Parameter
from cyclopts.validators import Number
from typing import Annotated

# Custom type with range validation
Percentage = Annotated[float, Number(min=0.0, max=100.0)]

app = App()

@app.command
def set_threshold(value: Percentage):
    """Set threshold as a percentage."""
    print(f"Threshold set to {value}%")

Using Pre-built Types

from cyclopts import App
from cyclopts.types import ExistingFile, PositiveInt, Email

app = App()

@app.command
def process_user_data(
    input_file: ExistingFile,
    max_records: PositiveInt,
    notify_email: Email
):
    """Process user data file."""
    print(f"Processing {input_file} (max {max_records} records)")
    print(f"Will notify {notify_email}")

Custom Converter Example

from cyclopts import App, Parameter
from datetime import datetime

def parse_date(value: str) -> datetime:
    """Custom date converter."""
    return datetime.strptime(value, "%Y-%m-%d")

app = App()

@app.command
def schedule_task(
    name: str,
    due_date: datetime = Parameter(converter=parse_date, help="Date in YYYY-MM-DD format")
):
    """Schedule a task with due date."""
    print(f"Task '{name}' scheduled for {due_date.strftime('%Y-%m-%d')}")

Multiple Validators

from cyclopts import App, Parameter
from cyclopts.validators import Number, LimitedChoice

app = App()

@app.command  
def configure_server(
    port: int = Parameter(
        validator=[
            Number(min=1024, max=65535),
            LimitedChoice(8080, 8443, 9000, 9443)
        ],
        help="Server port (must be 1024-65535 and one of: 8080, 8443, 9000, 9443)"
    )
):
    """Configure server with validated port."""
    print(f"Server configured on port {port}")

Install with Tessl CLI

npx tessl i tessl/pypi-cyclopts

docs

advanced-features.md

arguments-parameters.md

configuration.md

core-app.md

exceptions.md

index.md

types-validation.md

tile.json