Skip to content

Command Overload #94

Description

@PenguinBoi12

There is currently no way to register multiple implementations of the same command. Users who want to vary behaviour based on the number of arguments or their types must branch manually inside a single handler.

The idea is to introduce an @bot.overload() decorator that registers an alternate signature for an existing command. Rather than a separate structure, _commands itself would become two-level: Dict[str, Dict[Signature + TypeHints, Command]]. At dispatch time the bot looks up by name, then walks the inner dict to find the entry whose signature (parameter count and declared types) best matches the parsed arguments.

Proposed API

@bot.command()
async def greet(ctx, name: str):
    await ctx.reply(f"Hello {name}!")

@bot.overload()
async def greet(ctx):
    await ctx.reply("Hello World!")
@bot.command("roman")
async def roman_converter(ctx, number: str):
    result = roman_to_decimal(number)
    await ctx.reply(f"Roman -> Decimal: {result}!")

@bot.overload()
async def roman_converter(ctx, number: int):
    result = decimal_to_roman(number)
    await ctx.reply(f"Decimal -> Roman: {result}!")
!roman VI   → Roman -> Decimal: 6!
!roman 6    → Decimal -> Roman: VI!

Proposed Solution
_commands currently maps str and Command, so the type changes to Dict[str, Dict[tuple[type, ...], Command]]. The inner key is a tuple of parameter types derived from what Command already computes in its callback setter, tuple(type_hints.get(p.name, str) for p in params), with ctx already excluded. register_command raises AlreadyRegisteredError on duplicate names today, so both it and any callers that do a plain _commands[name] lookup need to be updated.

A few things worth particular attention during implementation: unannotated parameters default to str (matching existing _convert_type behaviour), meaning (ctx, name) and (ctx, name: str) would produce the same key and collide, registration should raise an error in that case. *args (VAR_POSITIONAL) doesn't map cleanly to a fixed-length tuple and may need a sentinel value or be excluded from overload dispatch entirely in a first version. Specific types (int, float, custom resolvers) must be tried before str regardless of registration order.

⚠️ There is still lots of unknown to be discussed. Anyone working on this issue should propose a complete solution before implementing it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Fields

    No fields configured for Feature.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions