Skip to content

Commit 7ddd853

Browse files
authored
Merge branch 'main' into feature/payload-respond
2 parents bf59e98 + 28be549 commit 7ddd853

File tree

1 file changed

+80
-1
lines changed

1 file changed

+80
-1
lines changed

twitchio/ext/commands/core.py

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,10 @@ def __init__(
204204
name: str,
205205
**kwargs: Unpack[CommandOptions],
206206
) -> None:
207+
self._signature: str | None
208+
207209
self._name: str = name
210+
self._parent: Group[Component_T, P] | None = kwargs.get("parent")
208211
self.callback = callback
209212
self._aliases: list[str] = kwargs.get("aliases", [])
210213
self._guards: list[Callable[..., bool] | Callable[..., CoroC]] = getattr(callback, "__command_guards__", [])
@@ -215,7 +218,6 @@ def __init__(
215218
self._injected: Component_T | None = None
216219
self._error: Callable[[Component_T, CommandErrorPayload], Coro] | Callable[[CommandErrorPayload], Coro] | None = None
217220
self._extras: dict[Any, Any] = kwargs.get("extras", {})
218-
self._parent: Group[Component_T, P] | None = kwargs.get("parent")
219221
self._bypass_global_guards: bool = kwargs.get("bypass_global_guards", False)
220222

221223
self._before_hook: Callable[[Component_T, Context[Any]], Coro] | Callable[[Context[Any]], Coro] | None = None
@@ -234,6 +236,82 @@ async def __call__(self, context: Context[BotT]) -> Any:
234236
callback = self._callback(self._injected, context) if self._injected else self._callback(context) # type: ignore
235237
return await callback # type: ignore will fix later
236238

239+
def _get_signature(self) -> None:
240+
params: dict[str, inspect.Parameter] | None = getattr(self, "_params", None)
241+
if not params:
242+
self._signature = None
243+
return
244+
245+
help_sig = ""
246+
for name, param in params.items():
247+
s = "<{}>"
248+
249+
if param.default is not param.empty:
250+
s = "[{}]"
251+
252+
if param.kind == param.KEYWORD_ONLY:
253+
s = s.format(f"...{name}")
254+
255+
elif param.kind == param.VAR_POSITIONAL:
256+
s = s.format(f"{name}...")
257+
258+
elif param.kind == param.POSITIONAL_OR_KEYWORD:
259+
s = s.format(f"{name}")
260+
261+
help_sig += f" {s}"
262+
263+
self._signature = help_sig
264+
265+
@property
266+
def parameters(self) -> MappingProxyType[str, inspect.Parameter]:
267+
"""Property returning a copy mapping of name to :class:`inspect.Parameter` pair, which are the parameters
268+
of the callback of this command, minus ``self`` (if in a class) and ``ctx``.
269+
270+
Can be used to retrieve the name, annotation, and default value of command parameters.
271+
"""
272+
return MappingProxyType(self._params)
273+
274+
@property
275+
def signature(self) -> str | None:
276+
"""Property returning an easily readable POSIX-like argument signature for the command.
277+
278+
Useful when generating or displaying help.
279+
280+
The format is described below:
281+
282+
- ``<arg>`` is a required argument.
283+
- ``[arg]`` is an optional argument.
284+
- ``...arg`` is a consume-rest argument.
285+
- ``arg...`` is an argument list.
286+
287+
Example
288+
-------
289+
290+
.. code:: python3
291+
292+
@commands.command()
293+
async def test(ctx: commands.Context, hello: str, world: int) -> None: ...
294+
...
295+
296+
print(test.signature) # <hello> <world>
297+
298+
299+
@commands.command()
300+
async def test(ctx: commands.Context, arg: str, *, other: str | None = None) -> None: ...
301+
...
302+
303+
print(test.signature) # <arg> [...other]
304+
305+
306+
@commands.command()
307+
async def test(ctx: commands.Context, arg: str, other: str | None = None, *args: str | None = None) -> None: ...
308+
...
309+
310+
print(test.signature) # <arg> [other] [args...]
311+
312+
"""
313+
return self._signature
314+
237315
@property
238316
def help(self) -> str:
239317
"""Property returning a :class:`str` which is the docstring associated with the command callback.
@@ -351,6 +429,7 @@ def callback(
351429
globalns = {}
352430

353431
self._params: dict[str, inspect.Parameter] = get_signature_parameters(func, globalns)
432+
self._get_signature()
354433

355434
def _convert_literal_type(
356435
self, context: Context[BotT], param: inspect.Parameter, args: tuple[Any, ...], *, raw: str | None

0 commit comments

Comments
 (0)