def validate_invoke(invoke): """ Validates the given invoker. Parameters ---------- invoke : `callable` The invoker to validate. Raises ------ TypeError If `invoke` is not async callable or accepts not 1 parameter. """ if invoke is None: raise TypeError(f'`invoke` function cannot be `None`.') analyzer = CallableAnalyzer(invoke, as_method=True) if not analyzer.is_async(): raise TypeError( f'`invoke` can be `CoroutineFunction`, got {invoke!r}.') min_, max_ = analyzer.get_non_reserved_positional_parameter_range() if min_ > 1: raise TypeError( f'`invoke` should accept `1` parameter, meanwhile the given one expects at ' f'least `{min_!r}`, got {invoke!r}.') if min_ != 1: if max_ < 1: if not analyzer.accepts_args(): raise TypeError( f'`invoke` should accept `1` parameters, meanwhile the given one expects ' f'up to `{max_!r}`, got {invoke!r}.')
def _validate_random_error_message_getter(random_error_message_getter): """ Validates the given `random_error_message_getter`. It should accept no parameters and return a string. Parameters ---------- random_error_message_getter : `callable` The random message getter to validate. Raises ------ TypeError - If `random_error_message_getter` is not callable. - If `random_error_message_getter` is not async. - If `random_error_message_getter` excepts not `0` parameters. """ analyzer = CallableAnalyzer(random_error_message_getter) if analyzer.is_async(): raise TypeError( f'`random_error_message_getter` cannot be async, got {random_error_message_getter!r}.' ) min_, max_ = analyzer.get_non_reserved_positional_parameter_range() if (min_ > 0): raise TypeError( f'A `random_error_message_getter` should accept `0` parameters, meanwhile ' f'{random_error_message_getter!r} accepts between `{min_}` and `{max_}`.' )
def validate_initial_invoke(initial_invoke): """ Validates the given default content getter. Parameters ---------- initial_invoke : `callable` The default content getter to validate. Raises ------ TypeError If `initial_invoke` is not async callable or accepts any parameters. """ if initial_invoke is None: raise TypeError(f'`initial_invoke` function cannot be `None`.') analyzer = CallableAnalyzer(initial_invoke, as_method=True) if not analyzer.is_async(): raise TypeError( f'`initial_invoke` should have be `async` function, got {initial_invoke!r}.' ) min_, max_ = analyzer.get_non_reserved_positional_parameter_range() if min_ > 0: raise TypeError( f'`initial_invoke` should accept `0` parameters, meanwhile the given one expects at ' f'least `{min_!r}`, got {initial_invoke!r}.') if min_ != 0: if max_ < 0: if not analyzer.accepts_args(): raise TypeError( f'`initial_invoke` should accept `0` parameters, meanwhile the given one ' f'expects up to `{max_!r}`, got {initial_invoke!r}.')
def validate_close(close): """ Validates the given closer. Parameters ---------- close : `callable` The closer to validate. Raises ------ TypeError If `close` is not async callable or accepts not 1 parameter. """ if close is None: raise TypeError(f'`close` function cannot be `None`.') analyzer = CallableAnalyzer(close, as_method=True) if not analyzer.is_async(): raise TypeError( f'`close` should have be `async` function, got {close!r}.') min_, max_ = analyzer.get_non_reserved_positional_parameter_range() if min_ > 1: raise TypeError( f'`close` should accept `1` parameters, meanwhile the given one expects at ' f'least `{min_!r}`, got {close!r}.') if min_ != 1: if max_ < 1: if not analyzer.accepts_args(): raise TypeError( f'`close` should accept `1` parameters, meanwhile the given one expects ' f'up to `{max_!r}`, got {close!r}.')
def validate_check(check): """ Validates the given check. Parameters ---------- check : `None` of `callable` The check to validate. Raises ------ TypeError If `check` is not `None` neither a non-async function accepting 1 parameter. """ if check is None: return analyzer = CallableAnalyzer(check, as_method=True) if analyzer.is_async(): raise TypeError( f'`check` should have NOT be be `async` function, got {check!r}.') min_, max_ = analyzer.get_non_reserved_positional_parameter_range() if min_ > 1: raise TypeError( f'`check` should accept `1` parameters, meanwhile the given one expects at ' f'least `{min_!r}`, got {check!r}.') if min_ != 1: if max_ < 1: if not analyzer.accepts_args(): raise TypeError( f'`check` should accept `1` parameters, meanwhile the given one expects ' f'up to `{max_!r}`, got `{check!r}`.')
def test_exception_handler(exception_handler): """ Tests whether the given exception handler accepts the expected amount of parameters. Parameters ---------- exception_handler : `CoroutineFunction` A function, which handles an exception and returns whether handled it. The following parameters are passed to it: +-------------------+-------------------------------------------------------------------------------+ | Name | Type | +===================+===============================================================================+ | client | ``Client`` | +-------------------+-------------------------------------------------------------------------------+ | interaction_event | ``InteractionEvent`` | +-------------------+-------------------------------------------------------------------------------+ | command | ``ComponentCommand``, ``SlasherApplicationCommand``, | | | ``SlasherApplicationCommandFunction``, ``SlasherApplicationCommandCategory``, | | | ``SlasherApplicationCommandParameterAutoCompleter`` | +-------------------+-------------------------------------------------------------------------------+ | exception | `BaseException` | +-------------------+-------------------------------------------------------------------------------+ Should return the following parameters: +-------------------+-----------+ | Name | Type | +===================+===========+ | handled | `bool` | +-------------------+-----------+ Raises ------ TypeError - If `exception_handler` accepts bad amount of parameters. - If `exception_handler` is not a coroutine function. """ analyzer = CallableAnalyzer(exception_handler) if not analyzer.is_async(): raise TypeError( f'`exception_handler` can be `async` function, got {exception_handler!r}.' ) min_, max_ = analyzer.get_non_reserved_positional_parameter_range() if min_ > 4: raise TypeError( f'`exception_handler` should accept `4` parameters, meanwhile the given callable expects at ' f'least `{min_!r}`, got {exception_handler!r}.') if min_ != 4: if max_ < 4: if not analyzer.accepts_args(): raise TypeError( f'`exception_handler` should accept `4` parameters, meanwhile the given callable ' f'expects up to `{max_!r}`, got {exception_handler!r}.')
def __new__(cls, prefix=None, embed_postprocessor=None): """ Creates a new ``SubterraneanHelpCommand``. Parameters ---------- prefix : `None`, `str` = `None`, Optional Prefix inserted before commands's display name. embed_postprocessor : `None`, `callable` = `None`, Optional An embed post-processor, what is called with the autogenerated embeds. The following `2` parameters are passed to it: +-----------------------+-------------------------------+ | Respective name | Type | +=======================+===============================+ | command_context | ``CommandContext`` | +-----------------------+-------------------------------+ | command_context | ``Command``, ``Category`` | +-----------------------+-------------------------------+ Raises ------ TypeError If `embed_postprocessor` is given, but not as a `non-async` `callable` accepting `2` parameters. """ if (embed_postprocessor is not None): analyzer = CallableAnalyzer(embed_postprocessor) min_, max_ = analyzer.get_non_reserved_positional_parameter_range() if min_ > 3: raise TypeError( f'`embed_postprocessor` should accept `3` parameters: `command_context, ' f'command_context`, meanwhile the given one expects at least `{min_!r}`, got ' f'{embed_postprocessor!r}.') if (min_ != 2) and (max_ < 2) and (not analyzer.accepts_args()): raise TypeError( f'`embed_postprocessor` should accept `2` parameters: `command_context, ' f'command_context`, meanwhile the given one expects up to `{max_!r}`, got ' f'{embed_postprocessor!r}.') if analyzer.is_async(): raise TypeError( f'`embed_postprocessor` cannot be async, got {embed_postprocessor!r}.' ) if prefix is None: prefix = DEFAULT_PREFIX else: prefix = preconvert_str(prefix, 'prefix', 1, 32) self = object.__new__(cls) self.embed_postprocessor = embed_postprocessor self.prefix = prefix return self
def test_precheck(precheck): """ Tests whether the given precheck accepts the expected amount of parameters. Parameters ---------- precheck : `callable` A function, which decides whether a received message should be processed. The following parameters are passed to it: +-----------+---------------+ | Name | Type | +===========+===============+ | client | ``Client`` | +-----------+---------------+ | message | ``Message`` | +-----------+---------------+ Should return the following parameters: +-------------------+-----------+ | Name | Type | +===================+===========+ | should_process | `bool` | +-------------------+-----------+ Raises ------ TypeError - If `precheck` accepts bad amount of parameters. - If `precheck` is async. """ analyzer = CallableAnalyzer(precheck) if analyzer.is_async(): raise TypeError( f'`precheck` should not be given as `async` function, got {precheck!r}.' ) min_, max_ = analyzer.get_non_reserved_positional_parameter_range() if min_ > 2: raise TypeError( f'`precheck` should accept `2` parameters, meanwhile the given callable expects at ' f'least `{min_!r}`, got {precheck!r}.') if min_ != 2: if max_ < 2: if not analyzer.accepts_args(): raise TypeError( f'`precheck` should accept `2` parameters, meanwhile the given callable expects ' f'up to `{max_!r}`, got {precheck!r}.')
def test_error_handler(error_handler): """ Tests whether the given error handler accepts the expected amount of parameters. Parameters ---------- error_handler : `callable` A function, which handles an error and returns whether handled it. The following parameters are passed to it: +-------------------+-----------------------+ | Name | Type | +===================+=======================+ | command_context | ``CommandContext`` | +-------------------+-----------------------+ | exception | `BaseException` | +-------------------+-----------------------+ Should return the following parameters: +-------------------+-----------+ | Name | Type | +===================+===========+ | handled | `bool` | +-------------------+-----------+ Raises ------ TypeError - If `error_handler` accepts bad amount of parameters. - If `error_handler` is not async. """ analyzer = CallableAnalyzer(error_handler) if not analyzer.is_async(): raise TypeError( f'`error_handler` can be `async` function, got {error_handler!r}') min_, max_ = analyzer.get_non_reserved_positional_parameter_range() if min_ > 2: raise TypeError( f'`error_handler` should accept `2` parameters, meanwhile the given callable expects at ' f'least `{min_!r}`, got {error_handler!r}') if min_ != 2: if max_ < 2: if not analyzer.accepts_args(): raise TypeError( f'`error_handler` should accept `2` parameters, meanwhile the given callable expects ' f'up to `{max_!r}`, got {error_handler!r}.')
def test_unknown_command(unknown_command): """ Tests whether the given unknown command handler accepts the expected amount of parameters. Parameters ---------- unknown_command : `callable` A function, which is called when no command is found. The following parameters are passed to it: +---------------+---------------+ | Name | Type | +===============+===============+ | client | ``Client`` | +---------------+---------------+ | message | ``Message`` | +---------------+---------------+ | command_name | `str` | +---------------+---------------+ Raises ------ TypeError - If `unknown_command` accepts bad amount of parameters. - If `unknown_command` is not async. """ analyzer = CallableAnalyzer(unknown_command) if not analyzer.is_async(): raise TypeError( f'`unknown_command` can be `async` function, got {unknown_command!r}.' ) min_, max_ = analyzer.get_non_reserved_positional_parameter_range() if min_ > 3: raise TypeError( f'`unknown_command` should accept `2` parameters, meanwhile the given callable expects at ' f'least `{min_!r}`, got {unknown_command!r}.') if min_ != 3: if max_ < 3: if not analyzer.accepts_args(): raise TypeError( f'`unknown_command` should accept `2` parameters, meanwhile the given callable expects ' f'up to `{max_!r}`, got {unknown_command!r}.')
def __new__(cls, parameter_count, default_handler=None, callback=None): """ Creates a new event handler instance form the given parameters. Parameters --------- parameter_count : `int` How much parameters does the event handler should accept. default_handler : `None`, `async-callable` = `None`, Optional Default handler to add by default. callback : `None`, `callable` = `None`, Optional Optional function to run when an event handler is added or removed. Raises ------ TypeError - If `parameter_count` is not `int`. - If `default_handler` is neither `None`, `async-callable` and cannot be instanced to async callable either. - If `default_handler` accepts different amount of parameters as `parameter_count` defined. ValueError - If `parameter_count` is negative. """ # Check `parameter_count` if type(parameter_count) is int: pass elif isinstance(parameter_count, int): parameter_count = int(parameter_count) else: raise TypeError( f'`parameter_count` can be `int`, got {parameter_count.__class__.__name__}; {parameter_count!r}.' ) if (parameter_count < 0): raise ValueError( f'`parameter_count` cannot be negative, got {parameter_count!r}.' ) # Check `default_handler` if (default_handler is None): instance_default_handler = False else: while True: analyzer = CallableAnalyzer(default_handler) if analyzer.is_async(): min_, max_ = analyzer.get_non_reserved_positional_parameter_range( ) if min_ <= parameter_count: if min_ == parameter_count: instance_default_handler = False break # min < expected if max_ >= parameter_count: instance_default_handler = False break if analyzer.accepts_args(): instance_default_handler = False break raise TypeError( f'`default_handler` should accept `{parameter_count!r}` parameters as defined, ' f'but it accepts [{min_!r}:{max_!r}], got {default_handler!r}.' ) if analyzer.can_instance_to_async_callable(): sub_analyzer = CallableAnalyzer(default_handler.__call__, as_method=True) if sub_analyzer.is_async(): min_, max_ = sub_analyzer.get_non_reserved_positional_parameter_range( ) if min_ <= parameter_count: if min_ == parameter_count: instance_default_handler = True break # min < expected if max_ >= parameter_count: instance_default_handler = True break if sub_analyzer.accepts_args(): instance_default_handler = True break raise TypeError( f'`default_handler` should accept `{parameter_count!r}` parameters as ' f'defined, but after instancing it accepts [{min_!r}:{max_!r}], got ' f'{default_handler!r}.') raise TypeError( f'`default_handler` can be `None`, `async-callable`, instantiable to ' f'`async-callable`, got {default_handler!r}.') # Check `callback` # TODO self = object.__new__(cls) self.parameter_count = parameter_count self.default_handler = default_handler self.instance_default_handler = instance_default_handler self.callback = callback return self
def __new__(cls, color=None, prefix=None, embed_postprocessor=None): """ Creates a new ``SubterraneanHelpCommand``. Parameters ---------- color : `None`, ``Color``, `int`, `callable` = `None`, Optional A color for the generated embeds. If given as a `callable`, then `3` parameters will be passed to it: +-------------------+-------------------+ | Respective name | Parameter type | +===================+===================+ | client | ``Client`` | +-------------------+-------------------+ | message | ``Message`` | +-------------------+-------------------+ | name | `str` | +-------------------+-------------------+ Async and not async callables are supported as well. prefix : `None`, `str` = `None`, Optional Prefix inserted before commands's display name. embed_postprocessor : `None`, `callable` = `None`, Optional An embed post-processor, what is called with the autogenerated embeds. `3` parameters are passed to it, which are the following: +-------------------+-------------------+ | Respective name | Parameter type | +===================+===================+ | client | ``Client`` | +-------------------+-------------------+ | message | ``Message`` | +-------------------+-------------------+ | embed | ``Embed`` | +-------------------+-------------------+ Raises ------ TypeError If `color` was not given as `None`, `int`, neither as a `callable` accepting `3` parameters. If `embed_postprocessor` is given, but not as a `non-async` `callable` accepting `3` parameters. ValueError If the `color` was given as `int`, but it's value is less than `0` or is over than `0xffffff`. """ color_getter = ColorGetter(color) if (embed_postprocessor is not None): analyzer = CallableAnalyzer(embed_postprocessor) min_, max_ = analyzer.get_non_reserved_positional_parameter_range() if min_ > 3: raise TypeError( f'`embed_postprocessor` should accept `3` parameters: `client, message, embed`, ' f'meanwhile the given one expects at least `{min_!r}`, got {embed_postprocessor!r}.' ) if (min_ != 3) and (max_ < 3) and (not analyzer.accepts_args()): raise TypeError( f'`embed_postprocessor` should accept `3` parameters: `client, message, embed`, ' f'meanwhile the given one expects up to `{max_!r}`, got {embed_postprocessor!r}.' ) if analyzer.is_async(): raise TypeError( f'`embed_postprocessor` cannot be async, got {embed_postprocessor}.' ) if prefix is None: prefix = DEFAULT_PREFIX else: prefix = preconvert_str(prefix, 'prefix', 1, 32) self = object.__new__(cls) self.color_getter = color_getter self.embed_postprocessor = embed_postprocessor self.prefix = prefix return self
def get_prefix_parser(prefix, prefix_ignore_case): """ Validates whether the given prefix is correct. prefix : `str`, `tuple` of `str`, `callable` Prefix of the command processor. Can be given as a normal `callable` or as an `async-callable` as well, which should accept `1` parameter: +-------------------+---------------+ | Name | Type | +===================+===============+ | message | ``Message`` | +-------------------+---------------+ And return the following value: +-------------------+---------------------------+ | Name | Type | +===================+===========================+ | prefix | `str`, `tuple` of `str` | +-------------------+---------------------------+ Returns ------- prefix_parser : `async-callable` Async callable prefix parser. Accepts the following parameters: +-----------+---------------+ | Name | Type | +===========+===============+ | message | ``Message`` | +-----------+---------------+ Returns the given values: +-----------+-------------------+ | Name | Type | +===========+===================+ | prefix | `None`, `str` | +-----------+-------------------+ | end | `int` | +-----------+-------------------+ prefix_getter : `async-callable` Accepts the following parameters: +-----------+---------------+ | Name | Type | +===========+===============+ | message | ``Message`` | +-----------+---------------+ Returns the given values: +-----------+-------------------+ | Name | Type | +===========+===================+ | prefix | `None`, `str` | +-----------+-------------------+ Raises ------ TypeError - Prefix's type is incorrect. - Prefix is a callable but accepts bad amount of parameters. """ if prefix_ignore_case: re_flags = re_ignore_case else: re_flags = 0 re_flags |= re_multi_line | re_dotall if callable(prefix): analyzed = CallableAnalyzer(prefix) non_reserved_positional_parameter_count = analyzed.get_non_reserved_positional_parameter_count( ) if non_reserved_positional_parameter_count != 1: raise TypeError( f'Callable `prefix` should accept `1` non reserved positional parameter, meanwhile it ' f'accepts: {non_reserved_positional_parameter_count}, got {prefix!r}.' ) if analyzed.is_async(): prefix_wrapper_function = prefix_wrapper_async_callable prefix_getter_function = prefix_getter_async_callable else: prefix_wrapper_function = prefix_wrapper_sync_callable prefix_getter_function = prefix_getter_sync_callable prefix_parser = partial_func(prefix_wrapper_function, prefix, re_flags) prefix_getter = partial_func(prefix_getter_function, prefix) else: if isinstance(prefix, str): escaped_prefix = re_escape(prefix) elif isinstance(prefix, tuple): if len(prefix) == 0: raise ValueError(f'`prefix` cannot be an empty `tuple`.') escaped_prefix = '|'.join( re_escape(prefix_part) for prefix_part in prefix) prefix = prefix[0] else: raise TypeError( f'`prefix` can be `callable`, `async-callable`, `str`, `tuple` of `str`, got ' f'{prefix.__class__.__name__}; {prefix!r}.') compiled_prefix = re_compile(escaped_prefix, re_flags) prefix_parser = partial_func(prefix_wrapper_regex, compiled_prefix) prefix_getter = partial_func(prefix_getter_static, prefix) return prefix_parser, prefix_getter
def test_name_rule(rule, rule_name): """ Tests the given name rule and raises if it do not passes any requirements. Parameters ---------- rule : `None`, `function` The rule to test. A name rule should accept the following parameters: +-------+-------------------+ | Name | Type | +=======+===================+ | name | `str` | +-------+-------------------+ Should return the following ones: +-------+-------------------+ | Name | Type | +=======+===================+ | name | `str` | +-------+-------------------+ rule_name : `str` The name of the given rule. Raises ------ TypeError - If `rule` is not `None`, `function`. - If `rule` is `async` `function`. - If `rule` accepts bad amount of parameters. - If `rule` raised exception when `str` was passed to it. - If `rule` did not return `str`, when passing `str` to it. - If `nullable` is given as `True` and `rule` raised exception when `None` was passed to it. - If `nullable` is given as `True` and `rule` did not return `str`, when passing `None` to it. """ if rule is None: return rule_type = rule.__class__ if (rule_type is not FunctionType): raise TypeError( f'`{rule_name}` can be `{FunctionType.__name__}`, got {rule_type.__name__}; {rule!r}.' ) analyzed = CallableAnalyzer(rule) if analyzed.is_async(): raise TypeError( f'`{rule_name}` can be a non async function, got {rule!r}.') non_reserved_positional_parameter_count = analyzed.get_non_reserved_positional_parameter_count( ) if non_reserved_positional_parameter_count != 1: raise TypeError( f'`{rule_name}` should accept `1` non reserved positional parameters, meanwhile it expects ' f'{non_reserved_positional_parameter_count}, got {rule!r}.') if analyzed.accepts_args(): raise TypeError( f'`{rule_name}` should accept not expect args, meanwhile it does, got {rule!r}.' ) if analyzed.accepts_kwargs(): raise TypeError( f'`{rule_name}` should accept not expect kwargs, meanwhile it does, got {rule!r}.' ) non_default_keyword_only_parameter_count = analyzed.get_non_default_keyword_only_parameter_count( ) if non_default_keyword_only_parameter_count: raise TypeError( f'`{rule_name}` should accept `0` keyword only parameters, meanwhile it expects ' f'{non_default_keyword_only_parameter_count}, got {rule!r}.') try: result = rule('test-this-name') except BaseException as err: raise TypeError( f'Got unexpected exception meanwhile testing the given {rule_name}: {rule!r}.' ) from err if (type(result) is not str): raise TypeError( f'{rule_name}: {rule!r} did not return `str`, meanwhile testing it, got {result.__class__.__name__}.' )