from __future__ import annotations
import builtins
from collections.abc import Callable, Sequence
from dataclasses import dataclass, field
from enum import Enum
from functools import cached_property
from typing import Any, Literal
from objinspect import Class, Function, Method
from interfacy.appearance.help_sort import HelpOptionSortRule, HelpSubcommandSortRule
from interfacy.appearance.layout import HelpLayout
from interfacy.executable_flag import ExecutableFlag
from interfacy.parameters import Param
from interfacy.pipe import PipeTargets
from interfacy.schema.value_plan import ArgumentValue
CommandType = Literal["function", "method", "class", "group", "instance"]
MODEL_DEFAULT_UNSET: Any = object()
[docs]
class ArgumentKind(str, Enum):
"""Classification of how a CLI argument is provided."""
POSITIONAL = "positional"
OPTION = "option"
[docs]
class ValueShape(str, Enum):
"""Shape of a parameter's parsed value."""
SINGLE = "single"
LIST = "list"
TUPLE = "tuple"
FLAG = "flag"
[docs]
class BooleanMode(str, Enum):
"""How a boolean CLI flag should be exposed."""
DUAL = "dual"
FLAG_ONLY = "flag_only"
[docs]
@dataclass
class BooleanBehavior:
"""
Metadata for boolean flags and their defaults.
Attributes:
supports_negative (bool): Whether a negative form is generated.
negative_form (str | None): Negative flag form (e.g., "--no-flag") if any.
default (bool | str | None): Effective default value for the flag.
Can be argparse.SUPPRESS (a str sentinel) to suppress the default.
mode (BooleanMode): Whether the flag is dual-form or one-way.
"""
supports_negative: bool
negative_form: str | None
default: bool | str | None
mode: BooleanMode = BooleanMode.DUAL
[docs]
@dataclass
class Argument:
"""
Schema entry describing a single CLI argument.
Attributes:
name (str): Canonical parameter name.
display_name (str): CLI-facing name or path.
kind (ArgumentKind): Whether the argument is positional or optional.
value_shape (ValueShape): Expected value shape for parsing.
flags (tuple[str, ...]): CLI flags or positional name display.
required (bool): Whether the argument must be provided.
default (Any): Default value or argparse sentinel.
help (str | None): Help text for display.
type (type[Any] | None): Parsed element type when applicable.
parser (Callable[[str], Any] | None): Parser for converting raw strings.
metavar (str | None): Custom metavar for help display.
nargs (str | int | None): Argparse nargs specifier.
boolean_behavior (BooleanBehavior | None): Boolean flag behavior details.
choices (Sequence[Any] | None): Allowed values, if any.
accepts_stdin (bool): Whether stdin can supply this value.
pipe_required (bool): Whether stdin is required for this value.
tuple_element_parsers (tuple[Callable[[str], Any], ...] | None): Per-element parsers.
is_expanded_from (str | None): Root model field name if expanded.
expansion_path (tuple[str, ...]): Nested path for expanded model fields.
original_model_type (type | None): Model type expanded into flags.
parent_is_optional (bool): Whether an ancestor model is optional.
model_default (Any): Default model instance or sentinel.
is_help_action (bool): Whether this argument is the parser's built-in help action.
value_plan (ArgumentValue | None): Internal conversion plan for nested values.
"""
name: str
display_name: str
kind: ArgumentKind
value_shape: ValueShape
flags: tuple[str, ...]
required: bool
default: Any
help: str | None
type: type[Any] | None
parser: Callable[[str], Any] | None
metavar: str | None = None
nargs: str | int | None = None
boolean_behavior: BooleanBehavior | None = None
choices: Sequence[Any] | None = None
accepts_stdin: bool = False
pipe_required: bool = False
tuple_element_parsers: tuple[Callable[[str], Any], ...] | None = None
is_expanded_from: str | None = None
expansion_path: tuple[str, ...] = ()
original_model_type: builtins.type[Any] | None = None
parent_is_optional: bool = False
model_default: Any = MODEL_DEFAULT_UNSET
is_help_action: bool = False
metadata: dict[str, Any] = field(default_factory=dict)
value_plan: ArgumentValue | None = None
[docs]
@dataclass
class Command:
"""
Schema entry describing a CLI command and its parameters.
Attributes:
obj (Class | Function | Method | None): Inspected callable backing the command.
canonical_name (str): Canonical command name.
cli_name (str): CLI-facing command name.
aliases (tuple[str, ...]): Alternative CLI names.
raw_description (str | None): Raw docstring description.
help_group (str | None): Optional help-only grouping label for command listings.
help_layout (HelpLayout | None): Help layout used for formatting.
pipe_targets (PipeTargets | None): Pipe target configuration for stdin.
parameters (list[Argument]): Argument specs for command parameters.
initializer (list[Argument]): Argument specs for class initialization.
subcommands (dict[str, Command] | None): Nested subcommands, if any.
executable_flags (list[ExecutableFlag]): Zero-argument executable flags.
raw_epilog (str | None): Raw epilog text for help output.
command_type (CommandType): Command category.
is_leaf (bool): Whether this command has no subcommands.
is_instance (bool): Whether the command comes from a stored instance.
parent_path (tuple[str, ...]): Command path for nested groups.
stored_instance (object | None): Stored instance for instance commands.
parameter_settings (dict[str, Param]): Per-parameter CLI setting overrides.
"""
obj: Class | Function | Method | None
canonical_name: str
cli_name: str
aliases: tuple[str, ...]
raw_description: str | None
help_group: str | None = None
help_layout: HelpLayout | None = None
pipe_targets: PipeTargets | None = None
parameters: list[Argument] = field(default_factory=list)
initializer: list[Argument] = field(default_factory=list)
subcommands: dict[str, Command] | None = None
executable_flags: list[ExecutableFlag] = field(default_factory=list)
raw_epilog: str | None = None
command_type: CommandType = "function"
is_leaf: bool = True
is_instance: bool = False
parent_path: tuple[str, ...] = ()
stored_instance: Any | None = None
include_inherited_methods: bool | None = None
include_protected_methods: bool | None = None
include_private_methods: bool | None = None
include_staticmethods: bool | None = None
include_classmethods: bool | None = None
method_skips: list[str] | None = None
expand_model_params: bool | None = None
model_expansion_max_depth: int | None = None
abbreviation_scope: Literal["top_level_options", "all_options"] | None = None
help_option_sort: list[HelpOptionSortRule] | None = None
help_subcommand_sort: list[HelpSubcommandSortRule] | None = None
help_option_sort_effective: list[HelpOptionSortRule] | None = None
help_subcommand_sort_effective: list[HelpSubcommandSortRule] | None = None
metadata: dict[str, Any] = field(default_factory=dict)
parameter_settings: dict[str, Param] = field(default_factory=dict)
@cached_property
def description(self) -> str | None:
"""Return the formatted description for help output."""
if self.raw_description is None:
return None
if self.help_layout is None:
return self.raw_description
return self.help_layout.format_description(self.raw_description)
@cached_property
def epilog(self) -> str | None:
"""Return the formatted epilog for help output."""
return self.raw_epilog
[docs]
@dataclass
class ParserSchema:
"""
Schema container for a complete CLI parser.
Attributes:
raw_description (str | None): Raw description string.
raw_epilog (str | None): Raw epilog string.
commands (dict[str, Command]): Command definitions keyed by canonical name.
command_key (str | None): Key used for command selection in parsed args.
allow_args_from_file (bool): Whether args can be read from files.
pipe_targets (PipeTargets | None): Default pipe target configuration.
theme (HelpLayout): Help layout for formatting.
commands_help (str | None): Pre-rendered command listing help.
metadata (dict[str, Any]): Additional parser metadata.
executable_flags (list[ExecutableFlag]): Parser-root executable flags.
help_option_sort_effective (list[HelpOptionSortRule] | None): Effective root option sort.
help_flags (tuple[str, ...]): Help flag aliases for synthetic help rows.
"""
raw_description: str | None
raw_epilog: str | None
commands: dict[str, Command]
command_key: str | None
allow_args_from_file: bool
pipe_targets: PipeTargets | None
theme: HelpLayout
commands_help: str | None = None
metadata: dict[str, Any] = field(default_factory=dict)
executable_flags: list[ExecutableFlag] = field(default_factory=list)
help_option_sort_effective: list[HelpOptionSortRule] | None = None
help_flags: tuple[str, ...] = ("--help",)
@cached_property
def description(self) -> str | None:
"""Return the formatted parser description."""
if self.raw_description is None:
return None
return self.theme.format_description(self.raw_description)
@cached_property
def epilog(self) -> str | None:
"""Return the formatted parser epilog."""
if self.raw_epilog is None:
return None
return self.theme.format_description(self.raw_epilog)
@property
def is_multi_command(self) -> bool:
"""Whether the schema contains multiple top-level commands."""
return len(self.commands) > 1
[docs]
def get_command(self, canonical_name: str) -> Command:
"""
Return the command definition for a canonical name.
Args:
canonical_name (str): Canonical command name.
"""
return self.commands[canonical_name]
@property
def canonical_names(self) -> Sequence[str]:
"""Return the canonical command names in schema order."""
return tuple(self.commands.keys())
__all__ = [
"MODEL_DEFAULT_UNSET",
"Argument",
"ArgumentKind",
"BooleanBehavior",
"BooleanMode",
"Command",
"CommandType",
"ExecutableFlag",
"ParserSchema",
"ValueShape",
]