Open1

Pydanticの四方山話

necoskijanennecoskijanen

コマンドライン引数 バリデーションエラーメッセージを読みやすくする

ツール使うのが自分だけならそのままで良いけれども。

サンプルコード
import argparse
from pydantic import BaseModel, Field, ValidationError, field_validator
from typing import List

# Pydantic model to define command-line arguments
class CliArgs(BaseModel):
    input_file: str = Field(..., description="Input file path")
    output_file: str = Field(description="Output file path (optional)")
    threshold: float = Field(gt=0, lt=1, description="Threshold value between 0 and 1")
    modes: List[str] = Field(min_items=1, description="At least one mode is required")

    # Custom validation for specific fields (example)
    @field_validator('output_file')
    def output_file_must_have_extension(cls, v):
        if v and '.' not in v:
            raise ValueError('Output file name must have an extension.')
        return v

# Function to generate user-friendly error messages
def format_validation_error(error: ValidationError) -> str:
    """Formats Pydantic's ValidationError into a readable format"""
    error_messages = ["Command-line argument error:"]
    for err in error.errors():
        field_name = " -> ".join(map(str, err['loc']))  # Handles nested fields
        message = err['msg']
        error_messages.append(f"  - Argument '{field_name}': {message}")
    return "\n".join(error_messages)

def main():
    # Parse arguments using argparse
    parser = argparse.ArgumentParser(description="Example of CLI argument validation using Pydantic")

    # While it's possible to dynamically add Pydantic model field definitions to argparse,
    # for simplicity, we define them manually here.
    # The 'description' can be used for help messages.
    parser.add_argument("--input-file", required=True, help="Input file path")
    parser.add_argument("--output-file", help="Output file path (optional)")
    parser.add_argument("--threshold", type=float, required=True, help="Threshold value between 0 and 1")
    parser.add_argument("--modes", nargs='+', required=True, help="At least one mode is required (space-separated)")

    args = parser.parse_args()

    try:
        # Convert argparse results to a dictionary and validate using the Pydantic model
        # argparse converts hyphens (-) to underscores (_), so we need to adjust for that
        args_dict = vars(args)
        # Match the field names of the Pydantic model (e.g., input-file -> input_file)
        validated_args = CliArgs(**args_dict)

        # Processing when validation is successful
        print("Argument validation successful:")
        print(validated_args.dict())
        # Write your actual processing logic here

    except ValidationError as e:
        # Processing when validation fails
        print(format_validation_error(e))
        print()
        parser.print_help()  # It's helpful to display the help message
        exit(1)  # Exit with an error code

if __name__ == "__main__":
    # Simulate command-line arguments for testing within the script
    import sys
    # Replace with your actual arguments
    sys.argv = [sys.argv[0], "--input-file", "input.txt", "--threshold", "0.5", "--modes", "mode1", "mode2"]  
    main()
メッセージ
Command-line argument error:
  - Argument 'output_file': Input should be a valid string

usage: colab_kernel_launcher.py [-h] --input-file INPUT_FILE
                                [--output-file OUTPUT_FILE] --threshold
                                THRESHOLD --modes MODES [MODES ...]

Example of CLI argument validation using Pydantic

options:
  -h, --help            show this help message and exit
  --input-file INPUT_FILE
                        Input file path
  --output-file OUTPUT_FILE
                        Output file path (optional)
  --threshold THRESHOLD
                        Threshold value between 0 and 1
  --modes MODES [MODES ...]
                        At least one mode is required (space-separated)