Skip to content

Introspect

ArgumentSchema dataclass

A data class for defining the schema of a CLI argument.

Attributes:

Name Type Description
name str

The name of the argument.

type str

The type of the argument.

required bool

Whether the argument is required.

key str

The key for the argument.

default MultiValueParamData | None

The default value of the argument.

choices Sequence[str] | None

The choices for the argument.

multiple bool

Whether the argument can have multiple values.

nargs int

The number of arguments for the argument.

Source code in src/guigaga/introspect.py
@dataclass
class ArgumentSchema:
    """
    A data class for defining the schema of a CLI argument.

    Attributes:
      name (str): The name of the argument.
      type (str): The type of the argument.
      required (bool): Whether the argument is required.
      key (str): The key for the argument.
      default (MultiValueParamData | None): The default value of the argument.
      choices (Sequence[str] | None): The choices for the argument.
      multiple (bool): Whether the argument can have multiple values.
      nargs (int): The number of arguments for the argument.
    """
    name: str
    type: str
    required: bool = False
    key: str = field(default_factory=generate_unique_id)
    default: MultiValueParamData | None = None
    choices: Sequence[str] | None = None
    multiple: bool = False
    nargs: int = 1

CommandSchema dataclass

A data class for defining the schema of a CLI command.

Attributes:

Name Type Description
name CommandName

The name of the command.

function Callable[..., Any | None]

The function to execute when the command is called.

key str

The key for the command.

docstring str | None

The docstring for the command.

options list[OptionSchema]

The options for the command.

arguments list[ArgumentSchema]

The arguments for the command.

subcommands dict['CommandName', 'CommandSchema']

The subcommands for the command.

parent CommandSchema | None

The parent command.

is_group bool

Whether the command is a group command.

Source code in src/guigaga/introspect.py
@dataclass
class CommandSchema:
    """
    A data class for defining the schema of a CLI command.

    Attributes:
      name (CommandName): The name of the command.
      function (Callable[..., Any | None]): The function to execute when the command is called.
      key (str): The key for the command.
      docstring (str | None): The docstring for the command.
      options (list[OptionSchema]): The options for the command.
      arguments (list[ArgumentSchema]): The arguments for the command.
      subcommands (dict["CommandName", "CommandSchema"]): The subcommands for the command.
      parent (CommandSchema | None): The parent command.
      is_group (bool): Whether the command is a group command.
    """
    name: CommandName
    function: Callable[..., Any | None]
    key: str = field(default_factory=generate_unique_id)
    docstring: str | None = None
    options: list[OptionSchema] = field(default_factory=list)
    arguments: list[ArgumentSchema] = field(default_factory=list)
    subcommands: dict["CommandName", "CommandSchema"] = field(default_factory=dict)
    parent: "CommandSchema | None" = None
    is_group: bool = False

    @property
    def path_from_root(self) -> list["CommandSchema"]:
        """
        Gets the path from the root command to the current command.

        Returns:
          list[CommandSchema]: A list of CommandSchema instances representing the path from the root command to the current command.
        """
        node = self
        path = [self]
        while True:
            node = node.parent
            if node is None:
                break
            path.append(node)
        return list(reversed(path))

path_from_root: list['CommandSchema'] property

Gets the path from the root command to the current command.

Returns:

Type Description
list['CommandSchema']

list[CommandSchema]: A list of CommandSchema instances representing the path from the root command to the current command.

MultiValueParamData dataclass

A data class for storing multiple values from a command line interface (CLI) option.

Attributes:

Name Type Description
values list[tuple[int | float | str]]

A list of tuples containing the values.

Source code in src/guigaga/introspect.py
@dataclass
class MultiValueParamData:
    """
    A data class for storing multiple values from a command line interface (CLI) option.

    Attributes:
      values (list[tuple[int | float | str]]): A list of tuples containing the values.
    """
    values: list[tuple[int | float | str]]

    @staticmethod
    def process_cli_option(value) -> "MultiValueParamData":
        """
        Processes a CLI option value into a MultiValueParamData instance.

        Args:
          value (Any): The value to process.

        Returns:
          MultiValueParamData: A MultiValueParamData instance containing the processed value.

        Examples:
          >>> MultiValueParamData.process_cli_option(('a', 'b', 'c'))
          MultiValueParamData(values=[('a', 'b', 'c')])
        """
        if value is None:
            value = MultiValueParamData([])
        elif isinstance(value, tuple):
            value = MultiValueParamData([value])
        elif isinstance(value, list):
            processed_list = [
                (item,) if not isinstance(item, tuple) else item for item in value
            ]
            value = MultiValueParamData(processed_list)
        else:
            value = MultiValueParamData([(value,)])

        return value

process_cli_option(value) staticmethod

Processes a CLI option value into a MultiValueParamData instance.

Parameters:

Name Type Description Default
value Any

The value to process.

required

Returns:

Name Type Description
MultiValueParamData 'MultiValueParamData'

A MultiValueParamData instance containing the processed value.

Examples:

>>> MultiValueParamData.process_cli_option(('a', 'b', 'c'))
MultiValueParamData(values=[('a', 'b', 'c')])
Source code in src/guigaga/introspect.py
@staticmethod
def process_cli_option(value) -> "MultiValueParamData":
    """
    Processes a CLI option value into a MultiValueParamData instance.

    Args:
      value (Any): The value to process.

    Returns:
      MultiValueParamData: A MultiValueParamData instance containing the processed value.

    Examples:
      >>> MultiValueParamData.process_cli_option(('a', 'b', 'c'))
      MultiValueParamData(values=[('a', 'b', 'c')])
    """
    if value is None:
        value = MultiValueParamData([])
    elif isinstance(value, tuple):
        value = MultiValueParamData([value])
    elif isinstance(value, list):
        processed_list = [
            (item,) if not isinstance(item, tuple) else item for item in value
        ]
        value = MultiValueParamData(processed_list)
    else:
        value = MultiValueParamData([(value,)])

    return value

OptionSchema dataclass

A data class for defining the schema of a CLI option.

Attributes:

Name Type Description
name list[str]

The names of the option.

type ParamType

The type of the option.

default MultiValueParamData | None

The default value of the option.

required bool

Whether the option is required.

is_flag bool

Whether the option is a flag.

is_boolean_flag bool

Whether the option is a boolean flag.

flag_value Any

The value of the flag.

opts list

Additional options.

counting bool

Whether the option is counting.

secondary_opts list

Secondary options.

key str | tuple[str]

The key for the option.

help str | None

The help text for the option.

choices Sequence[str] | None

The choices for the option.

multiple bool

Whether the option can have multiple values.

multi_value bool

Whether the option is a multi-value option.

nargs int

The number of arguments for the option.

Source code in src/guigaga/introspect.py
@dataclass
class OptionSchema:
    """
    A data class for defining the schema of a CLI option.

    Attributes:
      name (list[str]): The names of the option.
      type (ParamType): The type of the option.
      default (MultiValueParamData | None): The default value of the option.
      required (bool): Whether the option is required.
      is_flag (bool): Whether the option is a flag.
      is_boolean_flag (bool): Whether the option is a boolean flag.
      flag_value (Any): The value of the flag.
      opts (list): Additional options.
      counting (bool): Whether the option is counting.
      secondary_opts (list): Secondary options.
      key (str | tuple[str]): The key for the option.
      help (str | None): The help text for the option.
      choices (Sequence[str] | None): The choices for the option.
      multiple (bool): Whether the option can have multiple values.
      multi_value (bool): Whether the option is a multi-value option.
      nargs (int): The number of arguments for the option.
    """
    name: list[str]
    type: ParamType
    default: MultiValueParamData | None = None
    required: bool = False
    is_flag: bool = False
    is_boolean_flag: bool = False
    flag_value: Any = ""
    opts: list = field(default_factory=list)
    counting: bool = False
    secondary_opts: list = field(default_factory=list)
    key: str | tuple[str] = field(default_factory=generate_unique_id)
    help: str | None = None
    choices: Sequence[str] | None = None
    multiple: bool = False
    multi_value: bool = False
    nargs: int = 1

    def __post_init__(self):
        """
        Post-initialization method for OptionSchema. Sets the multi_value attribute based on the type attribute.
        """
        self.multi_value = isinstance(self.type, click.Tuple)

__post_init__()

Post-initialization method for OptionSchema. Sets the multi_value attribute based on the type attribute.

Source code in src/guigaga/introspect.py
def __post_init__(self):
    """
    Post-initialization method for OptionSchema. Sets the multi_value attribute based on the type attribute.
    """
    self.multi_value = isinstance(self.type, click.Tuple)

generate_unique_id()

Generates a unique identifier.

Returns:

Name Type Description
str

A unique identifier string.

Examples:

>>> generate_unique_id()
'id_1234abcd'
Source code in src/guigaga/introspect.py
def generate_unique_id():
    """
    Generates a unique identifier.

    Returns:
      str: A unique identifier string.

    Examples:
      >>> generate_unique_id()
      'id_1234abcd'
    """
    return f"id_{str(uuid.uuid4())[:8]}"

introspect_click_app(app)

Introspect a Click application and build a data structure containing information about all commands, options, arguments, and subcommands, including the docstrings and command function references.

This function recursively processes each command and its subcommands (if any), creating a nested dictionary that includes details about options, arguments, and subcommands, as well as the docstrings and command function references.

Parameters:

Name Type Description Default
app click.BaseCommand

The Click application's top-level group or command instance.

required

Returns:

Type Description
dict[CommandName, CommandSchema]

Dict[str, CommandData]: A nested dictionary containing the Click application's

dict[CommandName, CommandSchema]

structure. The structure is defined by the CommandData TypedDict and its related

dict[CommandName, CommandSchema]

TypedDicts (OptionData and ArgumentData).

Source code in src/guigaga/introspect.py
def introspect_click_app(app: BaseCommand) -> dict[CommandName, CommandSchema]:
    """
    Introspect a Click application and build a data structure containing
    information about all commands, options, arguments, and subcommands,
    including the docstrings and command function references.

    This function recursively processes each command and its subcommands
    (if any), creating a nested dictionary that includes details about
    options, arguments, and subcommands, as well as the docstrings and
    command function references.

    Args:
        app (click.BaseCommand): The Click application's top-level group or command instance.

    Returns:
        Dict[str, CommandData]: A nested dictionary containing the Click application's
        structure. The structure is defined by the CommandData TypedDict and its related
        TypedDicts (OptionData and ArgumentData).
    """

    def process_command(
        cmd_name: CommandName, cmd_obj: click.Command, parent=None
    ) -> CommandSchema:
        cmd_data = CommandSchema(
            name=cmd_name,
            docstring=cmd_obj.help,
            function=cmd_obj.callback,
            options=[],
            arguments=[],
            subcommands={},
            parent=parent,
            is_group=isinstance(cmd_obj, click.Group),
        )

        for param in cmd_obj.params:
            default = MultiValueParamData.process_cli_option(param.default)
            if isinstance(param, (click.Option, click.core.Group)):
                option_data = OptionSchema(
                    name=param.opts,
                    type=param.type,
                    is_flag=param.is_flag,
                    is_boolean_flag=param.is_bool_flag,
                    flag_value=param.flag_value,
                    counting=param.count,
                    opts=param.opts,
                    secondary_opts=param.secondary_opts,
                    required=param.required,
                    default=default,
                    help=param.help,
                    multiple=param.multiple,
                    nargs=param.nargs,
                )
                if isinstance(param.type, click.Choice):
                    option_data.choices = param.type.choices
                cmd_data.options.append(option_data)
            elif isinstance(param, click.Argument):
                argument_data = ArgumentSchema(
                    name=param.name,
                    type=param.type,
                    required=param.required,
                    multiple=param.multiple,
                    default=default,
                    nargs=param.nargs,
                )
                if isinstance(param.type, click.Choice):
                    argument_data.choices = param.type.choices
                cmd_data.arguments.append(argument_data)

        if isinstance(cmd_obj, click.core.Group):
            for subcmd_name, subcmd_obj in cmd_obj.commands.items():
                cmd_data.subcommands[CommandName(subcmd_name)] = process_command(
                    CommandName(subcmd_name), subcmd_obj, parent=cmd_data
                )

        return cmd_data

    data: dict[CommandName, CommandSchema] = {}

    # Special case for the root group
    if isinstance(app, click.Group):
        root_cmd_name = CommandName("root")
        data[root_cmd_name] = process_command(root_cmd_name, app)
        app = data[root_cmd_name]

    if isinstance(app, click.Group):
        for cmd_name, cmd_obj in app.commands.items():
            data[CommandName(cmd_name)] = process_command(
                CommandName(cmd_name), cmd_obj
            )
    elif isinstance(app, click.Command):
        cmd_name = CommandName(app.name)
        data[cmd_name] = process_command(cmd_name, app)

    return data