While reading more into type hinting in python, I played a little with it for field type validation for wamp procedures, the results are looking promising. The below snippet enables type validation for a wamp procedure, i.e. if io.crossbar.register is called with age as a float, it would raise etc.
comp = Component(transports="ws://localhost:8080/ws", realm="realm1")
def type_check(func):
async def _type_check(*args, **kwargs):
if args:
raise ValueError("Only accepts kwargs")
invalids = []
for key, value in typing.get_type_hints(func).items():
if key in kwargs and type(kwargs[key]) != value:
invalids.append(key)
if invalids:
raise ValueError(f"Invalid type of args: {' '.join(invalids)}")
return await func(**kwargs)
return _type_check
@comp.register("io.crossbar.register")
@type_check
def register(name: str, age: int, height: float):
print("Registration complete...")
run(comp)
This, if done at the right place (as part of ABPy API), could help remove lots of boilerplate code from Crossbar/CrossbarFX that only does field type validation.
Niiice=) yes, I agree, this looks promising. one itch: at least in some places, we do checks and raise an ApplicationError with a specific URI (the error class even supports error payload).
To replicate the custom error URI feature at least (not the custom per-error-instance payload though). we could extend the decorator. On the other hand, having different error URIs for different decorated procedures, when the errors are actually all about “type error on parameter” seems pointless …
How about just using wamp.error.invalid_argument ?
Also I have updated the code to be more reliable
def type_check(func):
async def _type_check(*args, **kwargs):
arguments = inspect.getcallargs(func, *args, **kwargs)
invalids = [name for name, kind in typing.get_type_hints(func).items()
if name in arguments and type(arguments[name]) != kind]
if invalids:
raise ValueError("Invalid type of args: {}".format(' '.join(invalids)),)
return await func(*args, **kwargs)
return _type_check