Skip to content

guidellm.utils.click_pydantic

RegistryAwareCommand

Bases: Command

Command subclass that enriches missing-argument errors for registry options.

When Click's parser raises BadOptionUsage for a registry-backed option (e.g. bare --constraint with no value), this override catches it and re-raises a BadParameter with the expected format and valid kinds.

Source code in src/guidellm/utils/click_pydantic.py
class RegistryAwareCommand(click.Command):
    """
    Command subclass that enriches missing-argument errors for registry options.

    When Click's parser raises ``BadOptionUsage`` for a registry-backed option
    (e.g. bare ``--constraint`` with no value), this override catches it and
    re-raises a ``BadParameter`` with the expected format and valid kinds.
    """

    def parse_args(self, ctx: click.Context, args: list[str]) -> list[str]:
        """
        Override to intercept missing-value errors for registry options.

        :param ctx: Click context
        :param args: Command-line arguments to parse
        :return: Remaining arguments after parsing
        """
        try:
            return super().parse_args(ctx, args)
        except click.BadOptionUsage as e:
            for param in self.params:
                if (
                    isinstance(param, RegistryConfigOption)
                    and param.registry_type is not None
                    and hasattr(e, "option_name")
                    and e.option_name in param.opts
                ):
                    usage = format_kind_config_usage(param.registry_type)
                    raise click.BadParameter(
                        f"option requires a value.\n{usage}",
                        ctx=ctx,
                        param=param,
                    ) from e
            raise

parse_args(ctx, args)

Override to intercept missing-value errors for registry options.

Parameters:

Name Type Description Default
ctx Context

Click context

required
args list[str]

Command-line arguments to parse

required

Returns:

Type Description
list[str]

Remaining arguments after parsing

Source code in src/guidellm/utils/click_pydantic.py
def parse_args(self, ctx: click.Context, args: list[str]) -> list[str]:
    """
    Override to intercept missing-value errors for registry options.

    :param ctx: Click context
    :param args: Command-line arguments to parse
    :return: Remaining arguments after parsing
    """
    try:
        return super().parse_args(ctx, args)
    except click.BadOptionUsage as e:
        for param in self.params:
            if (
                isinstance(param, RegistryConfigOption)
                and param.registry_type is not None
                and hasattr(e, "option_name")
                and e.option_name in param.opts
            ):
                usage = format_kind_config_usage(param.registry_type)
                raise click.BadParameter(
                    f"option requires a value.\n{usage}",
                    ctx=ctx,
                    param=param,
                ) from e
        raise

RegistryConfigOption

Bases: Option

Click option subclass that carries a reference to its registry type.

Used by :class:RegistryAwareCommand to enrich missing-argument errors with the expected format and valid kinds.

Source code in src/guidellm/utils/click_pydantic.py
class RegistryConfigOption(click.Option):
    """
    Click option subclass that carries a reference to its registry type.

    Used by :class:`RegistryAwareCommand` to enrich missing-argument errors
    with the expected format and valid kinds.
    """

    def __init__(
        self,
        *args: Any,
        registry_type: type[PydanticClassRegistryMixin] | None = None,  # type: ignore[type-arg]
        **kwargs: Any,
    ) -> None:
        self.registry_type = registry_type
        super().__init__(*args, **kwargs)

format_kind_config_usage(registry_type)

Build a self-documenting usage hint for a registry-backed config option.

Parameters:

Name Type Description Default
registry_type type[PydanticClassRegistryMixin]

The PydanticClassRegistryMixin subclass whose registered names are listed

required

Returns:

Type Description
str

Multi-line string describing expected format and valid kinds

Source code in src/guidellm/utils/click_pydantic.py
def format_kind_config_usage(
    registry_type: type[PydanticClassRegistryMixin],  # type: ignore[type-arg]
) -> str:
    """
    Build a self-documenting usage hint for a registry-backed config option.

    :param registry_type: The ``PydanticClassRegistryMixin`` subclass whose
        registered names are listed
    :return: Multi-line string describing expected format and valid kinds
    """
    disc = registry_type.schema_discriminator
    names = registry_type.registered_names()
    choices = ", ".join(names) if names else "(none registered)"
    return (
        f"Expected format: {disc}=<type>,key=value,... "
        f"or JSON/YAML object with a '{disc}' field\n"
        f"  Valid {disc}s: {choices}"
    )

format_validation_errors(ctx, err, base_class=None)

Translate a pydantic ValidationError into a click.BadParameter error.

Includes the top-level field names and full location paths, such as data[0].synthetic_text.output_tokens, so callers can identify which nested subfield failed rather than only the top-level CLI option.

For registry shape failures where the stock message does not list kinds (non-object input), appends a usage hint with expected format and valid kinds. Invalid kind tags are left as-is because pydantic already lists them.

Parameters:

Name Type Description Default
ctx Context

The active Click context

required
err ValidationError

The pydantic validation error to translate

required
base_class type[BaseModel] | None

Optional root model class for resolving argument aliases

None

Returns:

Type Description
BadParameter

A click.BadParameter with a formatted message and param hint

Source code in src/guidellm/utils/click_pydantic.py
def format_validation_errors(
    ctx: click.Context,
    err: ValidationError,
    base_class: type[BaseModel] | None = None,
) -> click.BadParameter:
    """
    Translate a pydantic ``ValidationError`` into a ``click.BadParameter`` error.

    Includes the top-level field names and full location paths, such as
    ``data[0].synthetic_text.output_tokens``, so callers can identify which
    nested subfield failed rather than only the top-level CLI option.

    For registry shape failures where the stock message does not list kinds
    (non-object input), appends a usage hint with expected format and valid kinds.
    Invalid ``kind`` tags are left as-is because pydantic already lists them.

    :param ctx: The active Click context
    :param err: The pydantic validation error to translate
    :param base_class: Optional root model class for resolving argument aliases
    :return: A ``click.BadParameter`` with a formatted message and param hint
    """
    errs = err.errors(include_url=False, include_context=True, include_input=True)

    param_names = set()
    msgs = []
    for e in errs:
        original_loc = e.get("loc", ())
        loc = original_loc
        msg = e.get("msg", "validation error")
        err_type = e.get("type")

        top_field: str | None = None
        if len(loc) > 0:
            if base_class is not None:
                with contextlib.suppress(KeyError):
                    loc, top_field = _resolve_param_name(loc, base_class)
            top_field = top_field or str(loc[0]) or None

            if top_field:
                param_names.add("--" + top_field.replace("_", "-"))

        formatted = _error_to_message(loc, msg)

        if (
            base_class is not None
            and err_type in _REGISTRY_SHAPE_ERROR_TYPES
            and (registry := _resolve_registry_type(original_loc, base_class))
            is not None
        ):
            formatted = f"{formatted}\n{format_kind_config_usage(registry)}"

        msgs.append(formatted)

    error_msg = "".join(f"\n  - {msg}" for msg in msgs)
    return click.BadParameter(error_msg, ctx=ctx, param_hint=list(param_names))

registry_option(*param_decls, registry, multiple=False, **extra)

Click option decorator for PydanticClassRegistryMixin-backed fields.

Produces a --name <config> option where <config> is a single comma-separated key=value string (e.g. kind=constant,rate=10).

Parameters:

Name Type Description Default
param_decls str

Click-style parameter declarations (e.g. "--output", "--output", "outputs")

()
registry type[PydanticClassRegistryMixin]

A PydanticClassRegistryMixin subclass whose registered names populate the metavar

required
multiple bool

When True, the option may be repeated and values are collected into a list of dicts

False
extra Any

Additional keyword arguments forwarded to click.option

{}

Returns:

Type Description

A decorator that adds the option to a Click command

Source code in src/guidellm/utils/click_pydantic.py
def registry_option(
    *param_decls: str,
    registry: type[PydanticClassRegistryMixin],  # type: ignore[type-arg]
    multiple: bool = False,
    **extra: Any,
):
    """
    Click option decorator for ``PydanticClassRegistryMixin``-backed fields.

    Produces a ``--name <config>`` option where ``<config>`` is a single
    comma-separated key=value string (e.g. ``kind=constant,rate=10``).

    :param param_decls: Click-style parameter declarations (e.g. ``"--output"``,
        ``"--output", "outputs"``)
    :param registry: A ``PydanticClassRegistryMixin`` subclass whose registered
        names populate the metavar
    :param multiple: When ``True``, the option may be repeated and values are
        collected into a list of dicts
    :param extra: Additional keyword arguments forwarded to ``click.option``
    :return: A decorator that adds the option to a Click command
    """
    param_type = _RegistryParamType(registry, registry.schema_discriminator)
    if multiple and "help" in extra:
        extra["help"] = f"{extra['help']}  [repeatable]"
    kwargs: dict[str, Any] = {
        "cls": RegistryConfigOption,
        "registry_type": registry,
        "type": param_type,
        "multiple": multiple,
        "callback": _make_common_field_callback(
            multiple, registry.schema_discriminator, registry
        ),
        "expose_value": True,
        "is_eager": False,
        **extra,
    }
    if not multiple:
        kwargs.setdefault("default", None)

    return click.option(*param_decls, **kwargs)

registry_options_from_model(model, group_key=None)

Decorator factory that introspects a Pydantic model and generates click.option decorators for each registry-backed field.

Each field in the model that has a schema_discriminator attribute gets a CLI option accepting a single comma-separated key=value string (e.g. --profile kind=constant,rate=10). All parsed values are aggregated into a group kwarg passed to the decorated function.

Parameters:

Name Type Description Default
model type[BaseModel]

A Pydantic model class whose fields are PydanticClassRegistryMixin subclasses or lists of them

required
group_key str | None

Optional key under which to group all registry values

None

Returns:

Type Description

A decorator that adds click options and wraps the function

Source code in src/guidellm/utils/click_pydantic.py
def registry_options_from_model(model: type[BaseModel], group_key: str | None = None):
    """
    Decorator factory that introspects a Pydantic model and generates
    ``click.option`` decorators for each registry-backed field.

    Each field in the model that has a ``schema_discriminator`` attribute gets a
    CLI option accepting a single comma-separated key=value string (e.g.
    ``--profile kind=constant,rate=10``).  All parsed values are aggregated
    into a group kwarg passed to the decorated function.

    :param model: A Pydantic model class whose fields are
        ``PydanticClassRegistryMixin`` subclasses or lists of them
    :param group_key: Optional key under which to group all registry values
    :return: A decorator that adds click options and wraps the function
    """
    field_specs = _introspect_registry_fields(model)

    def decorator(func):
        wrapped = func

        for spec in reversed(field_specs):
            cli_name = spec["argument_alias"] or spec["name"]
            option_name = f"--{cli_name.replace('_', '-')}"
            wrapped = registry_option(
                option_name,
                spec["name"],
                registry=spec["field_type"],
                multiple=spec["is_list"],
                help=spec["description"],
            )(wrapped)

        @functools.wraps(wrapped)
        def wrapper(**kw):
            ctx = click.get_current_context()
            group: dict[str, Any] = {}

            for spec in field_specs:
                value = kw.pop(spec["name"], None)
                source = ctx.get_parameter_source(spec["name"])
                if source is not None and source != click.core.ParameterSource.DEFAULT:
                    group[spec["name"]] = value

            if group:
                if group_key is not None:
                    kw[group_key] = group
                else:
                    kw.update(group)

            return func(**kw)

        if hasattr(wrapped, "__click_params__"):
            wrapper.__click_params__ = wrapped.__click_params__  # type: ignore[attr-defined]

        return wrapper

    return decorator