def test_function_info(): def f1(): pass async def f2(x): pass def f3(x, y=2): pass async def f4(x, y=2, *, z=3): pass def f5(x, y=2, *, z=3, **kwargs): pass async def f6(x, y=2, *args): pass def f7(x, *args, **kwargs): pass f8 = pow assert signature_info(f1) == SignatureInfo(0, 0, [], []) assert signature_info(f2) == SignatureInfo(1, 1, ['x'], []) assert signature_info(f3) == SignatureInfo(1, 2, ['x'], ['y']) assert signature_info(f4) == SignatureInfo(1, 2, ['x'], ['y', 'z']) assert signature_info(f5) == SignatureInfo(1, 2, ['x'], any) assert signature_info(f6) == SignatureInfo(1, None, ['x'], ['y']) assert signature_info(f7) == SignatureInfo(1, None, ['x'], any) assert signature_info(f8) == SignatureInfo(2, 3, [], None)
def handler_invocation(handler, request): method, args = request.method, request.args if handler is None: raise RPCError(JSONRPC.METHOD_NOT_FOUND, f'unknown method "{method}"') # We must test for too few and too many arguments. How # depends on whether the arguments were passed as a list or as # a dictionary. info = signature_info(handler) if isinstance(args, (tuple, list)): if len(args) < info.min_args: s = '' if len(args) == 1 else 's' raise RPCError.invalid_args( f'{len(args)} argument{s} passed to method ' f'"{method}" but it requires {info.min_args}') if info.max_args is not None and len(args) > info.max_args: s = '' if len(args) == 1 else 's' raise RPCError.invalid_args( f'{len(args)} argument{s} passed to method ' f'{method} taking at most {info.max_args}') return partial(handler, *args) # Arguments passed by name if info.other_names is None: raise RPCError.invalid_args(f'method "{method}" cannot ' f'be called with named arguments') missing = set(info.required_names).difference(args) if missing: s = '' if len(missing) == 1 else 's' missing = ', '.join(sorted(f'"{name}"' for name in missing)) raise RPCError.invalid_args(f'method "{method}" requires ' f'parameter{s} {missing}') if info.other_names is not any: excess = set(args).difference(info.required_names) excess = excess.difference(info.other_names) if excess: s = '' if len(excess) == 1 else 's' excess = ', '.join(sorted(f'"{name}"' for name in excess)) raise RPCError.invalid_args(f'method "{method}" does not ' f'take parameter{s} {excess}') return partial(handler, **args)