mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-04-21 08:17:47 +00:00
[commands] Require number of parameters at instantiation time
This allows it to bypass annotation evaluation for arguments that don't matter like self and context.
This commit is contained in:
parent
0b5c3cf256
commit
8226f0df2c
@ -129,7 +129,15 @@ def get_signature_parameters(function: Callable[..., Any], globalns: Dict[str, A
|
||||
params = {}
|
||||
cache: Dict[str, Any] = {}
|
||||
eval_annotation = discord.utils.evaluate_annotation
|
||||
for name, parameter in signature.parameters.items():
|
||||
required_params = discord.utils.is_inside_class(function) + 1
|
||||
if len(signature.parameters) < required_params:
|
||||
raise TypeError(f'Command signature requires at least {required_params - 1} parameter(s)')
|
||||
|
||||
iterator = iter(signature.parameters.items())
|
||||
for _ in range(0, required_params):
|
||||
next(iterator)
|
||||
|
||||
for name, parameter in iterator:
|
||||
annotation = parameter.annotation
|
||||
if annotation is parameter.empty:
|
||||
params[name] = parameter
|
||||
@ -638,21 +646,7 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
|
||||
|
||||
Useful for inspecting signature.
|
||||
"""
|
||||
result = self.params.copy()
|
||||
if self.cog is not None:
|
||||
# first parameter is self
|
||||
try:
|
||||
del result[next(iter(result))]
|
||||
except StopIteration:
|
||||
raise ValueError("missing 'self' parameter") from None
|
||||
|
||||
try:
|
||||
# first/second parameter is context
|
||||
del result[next(iter(result))]
|
||||
except StopIteration:
|
||||
raise ValueError("missing 'context' parameter") from None
|
||||
|
||||
return result
|
||||
return self.params.copy()
|
||||
|
||||
@property
|
||||
def full_parent_name(self) -> str:
|
||||
@ -727,20 +721,6 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
|
||||
view = ctx.view
|
||||
iterator = iter(self.params.items())
|
||||
|
||||
if self.cog is not None:
|
||||
# we have 'self' as the first parameter so just advance
|
||||
# the iterator and resume parsing
|
||||
try:
|
||||
next(iterator)
|
||||
except StopIteration:
|
||||
raise discord.ClientException(f'Callback for {self.name} command is missing "self" parameter.')
|
||||
|
||||
# next we have the 'ctx' as the next parameter
|
||||
try:
|
||||
next(iterator)
|
||||
except StopIteration:
|
||||
raise discord.ClientException(f'Callback for {self.name} command is missing "ctx" parameter.')
|
||||
|
||||
for name, param in iterator:
|
||||
ctx.current_parameter = param
|
||||
if param.kind in (param.POSITIONAL_OR_KEYWORD, param.POSITIONAL_ONLY):
|
||||
|
@ -1061,6 +1061,21 @@ def resolve_annotation(
|
||||
return evaluate_annotation(annotation, globalns, locals, cache)
|
||||
|
||||
|
||||
def is_inside_class(func: Callable[..., Any]) -> bool:
|
||||
# For methods defined in a class, the qualname has a dotted path
|
||||
# denoting which class it belongs to. So, e.g. for A.foo the qualname
|
||||
# would be A.foo while a global foo() would just be foo.
|
||||
#
|
||||
# Unfortuately, for nested functions this breaks. So inside an outer
|
||||
# function named outer, those two would end up having a qualname with
|
||||
# outer.<locals>.A.foo and outer.<locals>.foo
|
||||
|
||||
if func.__qualname__ == func.__name__:
|
||||
return False
|
||||
(remaining, _, _) = func.__qualname__.rpartition('.')
|
||||
return not remaining.endswith('<locals>')
|
||||
|
||||
|
||||
TimestampStyle = Literal['f', 'F', 'd', 'D', 't', 'T', 'R']
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user