Source code for argclass._factory

"""Factory functions for creating arguments."""

from pathlib import Path
from typing import (
    Any,
    Callable,
    Iterable,
    List,
    Literal,
    Optional,
    Type,
    TypeVar,
    Union,
    cast,
    overload,
)

from argparse import Action

from ._store import ConfigArgument, INIConfig, TypedArgument
from ._types import Actions, ConverterType, LogLevelEnum, Nargs, NargsType

T = TypeVar("T")

# Nargs literals that produce sequences
_NargsSequence = Union[Literal["+", "*"], Nargs, int]


# noinspection PyShadowingBuiltins
[docs] def ArgumentSingle( *aliases: str, type: Type[T], action: Union[Actions, Type[Action]] = Actions.default(), choices: Optional[Iterable[str]] = None, const: Optional[Any] = None, converter: Optional[Callable[[T], T]] = None, default: Optional[T] = None, env_var: Optional[str] = None, help: Optional[str] = None, metavar: Optional[str] = None, required: Optional[bool] = None, secret: bool = False, ) -> T: """ Create a single-value argument with precise typing. Use this when you need exact type inference for a single value. The `type` parameter is required and determines the return type. Example: class Parser(argclass.Parser): count: int = argclass.ArgumentSingle(type=int, default=10) name: str = argclass.ArgumentSingle(type=str) """ return cast( T, TypedArgument( action=action, aliases=aliases, choices=choices, const=const, converter=converter, default=default, secret=secret, env_var=env_var, help=help, metavar=metavar, nargs=None, required=required, type=type, ), )
# noinspection PyShadowingBuiltins
[docs] def ArgumentSequence( *aliases: str, type: Type[T], nargs: _NargsSequence = "+", action: Union[Actions, Type[Action]] = Actions.default(), choices: Optional[Iterable[str]] = None, const: Optional[Any] = None, converter: Optional[Callable[[List[T]], Any]] = None, default: Optional[List[T]] = None, env_var: Optional[str] = None, help: Optional[str] = None, metavar: Optional[str] = None, required: Optional[bool] = None, secret: bool = False, ) -> List[T]: """ Create a multi-value argument with precise typing. Use this when you need exact type inference for a list of values. The ``type`` parameter is required and determines the element type. Args: nargs: Number of values. Defaults to "+" (one or more). Use "*" for zero or more, or an int for exact count. Example:: class Parser(argclass.Parser): files: list[str] = argclass.ArgumentSequence(type=str) numbers: list[int] = argclass.ArgumentSequence( type=int, nargs="*", default=[] ) """ return cast( List[T], TypedArgument( action=action, aliases=aliases, choices=choices, const=const, converter=converter, default=default, secret=secret, env_var=env_var, help=help, metavar=metavar, nargs=nargs, required=required, type=type, ), )
# Overload: type + nargs (sequence) → List[T] @overload def Argument( *aliases: str, type: Type[T], nargs: _NargsSequence, action: Union[Actions, Type[Action]] = ..., choices: Optional[Iterable[str]] = ..., const: Optional[Any] = ..., converter: Optional[ConverterType] = ..., default: Optional[Any] = ..., env_var: Optional[str] = ..., help: Optional[str] = ..., metavar: Optional[str] = ..., required: Optional[bool] = ..., secret: bool = ..., ) -> List[T]: ... # Overload: type without nargs (single) → T @overload def Argument( *aliases: str, type: Type[T], action: Union[Actions, Type[Action]] = ..., choices: Optional[Iterable[str]] = ..., const: Optional[Any] = ..., converter: Optional[ConverterType] = ..., default: Optional[T] = ..., env_var: Optional[str] = ..., help: Optional[str] = ..., metavar: Optional[str] = ..., nargs: None = ..., required: Optional[bool] = ..., secret: bool = ..., ) -> T: ... # Overload: converter determines return type @overload def Argument( *aliases: str, converter: Callable[..., T], action: Union[Actions, Type[Action]] = ..., choices: Optional[Iterable[str]] = ..., const: Optional[Any] = ..., default: Optional[Any] = ..., env_var: Optional[str] = ..., help: Optional[str] = ..., metavar: Optional[str] = ..., nargs: NargsType = ..., required: Optional[bool] = ..., secret: bool = ..., type: Optional[ConverterType] = ..., ) -> T: ... # Overload: fallback @overload def Argument( *aliases: str, action: Union[Actions, Type[Action]] = ..., choices: Optional[Iterable[str]] = ..., const: Optional[Any] = ..., converter: Optional[ConverterType] = ..., default: Optional[Any] = ..., env_var: Optional[str] = ..., help: Optional[str] = ..., metavar: Optional[str] = ..., nargs: NargsType = ..., required: Optional[bool] = ..., secret: bool = ..., type: None = ..., ) -> Any: ... # noinspection PyShadowingBuiltins
[docs] def Argument( *aliases: str, action: Union[Actions, Type[Action]] = Actions.default(), choices: Optional[Iterable[str]] = None, const: Optional[Any] = None, converter: Optional[ConverterType] = None, default: Optional[Any] = None, env_var: Optional[str] = None, help: Optional[str] = None, metavar: Optional[str] = None, nargs: NargsType = None, required: Optional[bool] = None, secret: bool = False, type: Optional[ConverterType] = None, ) -> Any: """ Create a typed argument for a Parser or Group class. Dispatches to ArgumentSingle or ArgumentSequence based on nargs. Args: aliases: Command-line aliases (e.g., "-n", "--name"). action: How to handle the argument (store, store_true, etc.). choices: Restrict values to these options. const: Constant value for store_const/append_const actions. converter: Post-parse transform function. With nargs, receives the list. default: Default value if argument not provided. env_var: Environment variable to read default from. help: Help text for --help output. metavar: Placeholder name in help text. nargs: Number of values (int, "?", "*", "+", or Nargs enum). required: Whether the argument must be provided. secret: If True, hide value from help and wrap str in SecretString. type: Per-value converter for argparse. With nargs, called per value. Returns: TypedArgument instance. Example:: # type: converts each CLI value individually numbers = Argument(nargs="+", type=int) # Parsing ["1", "2"] -> calls int("1"), int("2") -> [1, 2] # converter: transforms the final result after type conversion unique = Argument(nargs="+", type=int, converter=set) # Parsing ["1", "2", "1"] -> [1, 2, 1] -> set([1, 2, 1]) -> {1, 2} # Combining type and converter for set[int]: class Parser(argclass.Parser): numbers: set[int] = Argument( type=int, # Convert each "1", "2" to int converter=set, # Convert [1, 2, 1] to {1, 2} nargs="+", ) # Alternative: single converter function for set[int]: class Parser(argclass.Parser): numbers: set[int] = Argument( converter=lambda vals: set(map(int, vals)), nargs="+", ) """ # Dispatch to typed functions when type is provided if type is not None: if nargs in ("+", "*") or isinstance(nargs, (int, Nargs)): return ArgumentSequence( *aliases, type=type, # type: ignore[arg-type] nargs=nargs, # type: ignore[arg-type] action=action, choices=choices, const=const, converter=converter, # type: ignore[arg-type] default=default, env_var=env_var, help=help, metavar=metavar, required=required, secret=secret, ) else: return ArgumentSingle( *aliases, type=type, # type: ignore[arg-type] action=action, choices=choices, const=const, converter=converter, # type: ignore[arg-type] default=default, env_var=env_var, help=help, metavar=metavar, required=required, secret=secret, ) # Fallback for untyped arguments return TypedArgument( action=action, aliases=aliases, choices=choices, const=const, converter=converter, default=default, secret=secret, env_var=env_var, help=help, metavar=metavar, nargs=nargs, required=required, type=type, )
[docs] def EnumArgument( enum_class: Type, *aliases: str, action: Union[Actions, Type[Action]] = Actions.default(), default: Optional[Any] = None, env_var: Optional[str] = None, help: Optional[str] = None, metavar: Optional[str] = None, nargs: NargsType = None, required: Optional[bool] = None, use_value: bool = False, lowercase: bool = False, ) -> Any: """ Create an argument from an Enum class. Args: enum_class: The Enum class to use for choices and conversion. aliases: Command-line aliases (e.g., "-l", "--level"). action: How to handle the argument. default: Default value (as string name, not enum member). env_var: Environment variable to read default from. help: Help text for --help output. metavar: Placeholder name in help text. nargs: Number of values. required: Whether the argument must be provided. use_value: If True, return enum.value instead of enum member. lowercase: If True, use lowercase choices and accept lowercase input. Returns: TypedArgument instance. """ if lowercase: choices = tuple(e.name.lower() for e in enum_class) else: choices = tuple(e.name for e in enum_class) def converter(x: Any) -> Any: # Handle existing enum members if isinstance(x, enum_class): return x.value if use_value else x # Convert string to enum name = x.upper() if lowercase else x member = enum_class[name] return member.value if use_value else member return TypedArgument( action=action, aliases=aliases, choices=choices, converter=converter, default=default, env_var=env_var, help=help, metavar=metavar, nargs=nargs, required=required, )
# noinspection PyShadowingBuiltins
[docs] def Secret( *aliases: str, action: Union[Actions, Type[Action]] = Actions.default(), choices: Optional[Iterable[str]] = None, const: Optional[Any] = None, converter: Optional[ConverterType] = None, default: Optional[Any] = None, env_var: Optional[str] = None, help: Optional[str] = None, metavar: Optional[str] = None, nargs: NargsType = None, required: Optional[bool] = None, type: Optional[ConverterType] = None, ) -> Any: """ Create a secret argument that masks sensitive values. Secret arguments are wrapped in SecretString, which: - Returns ``'******'`` for repr() to prevent accidental logging - Supports equality comparison without exposing the value - Use str() to access the actual value when needed Use ``parser.sanitize_env()`` after parsing to remove secret environment variables before spawning subprocesses. Args: aliases: Command-line aliases (e.g., "-p", "--password"). env_var: Environment variable to read the secret from. default: Default value if not provided. help: Help text (the actual value is never shown). Returns: TypedArgument with secret=True. Example:: class Parser(argclass.Parser): api_key: str = argclass.Secret(env_var="API_KEY") password: str = argclass.Secret() parser = Parser() parser.parse_args() parser.sanitize_env() # Remove secrets from environment # Safe: shows '******' print(f"API key: {parser.api_key!r}") # Access actual value connect(api_key=str(parser.api_key)) """ return Argument( # type: ignore[misc,call-overload] *aliases, action=action, choices=choices, const=const, converter=converter, # type: ignore[arg-type] default=default, env_var=env_var, help=help, metavar=metavar, nargs=nargs, required=required, secret=True, type=type, )
# noinspection PyShadowingBuiltins
[docs] def Config( *aliases: str, search_paths: Optional[Iterable[Union[Path, str]]] = None, choices: Optional[Iterable[str]] = None, converter: Optional[ConverterType] = None, const: Optional[Any] = None, default: Optional[Any] = None, env_var: Optional[str] = None, help: Optional[str] = None, metavar: Optional[str] = None, nargs: NargsType = None, required: Optional[bool] = None, config_class: Type[ConfigArgument] = INIConfig, ) -> Any: """ Create a configuration file argument. This creates a ``--config`` argument that loads structured data from a file. The loaded data is accessible as a dict-like object. Note: This is different from ``config_files`` parameter on Parser, which presets CLI argument defaults. This argument loads arbitrary configuration data for your application. Args: aliases: Command-line aliases (default: "--config"). search_paths: Default paths to search for config files. config_class: Parser class (INIConfig, JSONConfig, TOMLConfig). env_var: Environment variable for config file path. help: Help text for --help output. Returns: ConfigArgument instance. Example:: class Parser(argclass.Parser): config = argclass.Config(config_class=argclass.JSONConfig) parser = Parser() parser.parse_args(["--config", "settings.json"]) # Access loaded configuration db_host = parser.config["database"]["host"] """ return config_class( search_paths=search_paths, aliases=aliases, choices=choices, converter=converter, const=const, default=default, env_var=env_var, help=help, metavar=metavar, nargs=nargs, required=required, )
# Pre-built log level argument LogLevel = EnumArgument( LogLevelEnum, use_value=True, lowercase=True, default="info", )