diff --git a/src/replit/maqpy/utils.py b/src/replit/maqpy/utils.py index 4399014..95c47d9 100644 --- a/src/replit/maqpy/utils.py +++ b/src/replit/maqpy/utils.py @@ -1,6 +1,6 @@ """Utitilities to make development easier.""" from functools import wraps -from typing import Any, Callable +from typing import Any, Callable, Tuple import flask @@ -52,3 +52,36 @@ def needs_signin(func: Callable = None, login_html: str = sign_in_snippet) -> Ca return decorator(func) else: # called with options, eg @needs_signin(login_html='...') return decorator + + +def needs_params(*param_names: str, onerror: Callable[Tuple[str]] = None) -> Callable: + """Require paramaters before a handler can be activated. + + Args: + param_names (str): The paramaters that must be in the request. + onerror (Callable): A function to handle when a paramater is missing. + If no function is specified a handler that returns a descriptive error and + 400 Bad Request status code will be used. + + Raises: + TypeError: No paramaters were provided or an invalid one was provided. + NotImplementedError: If the handler is called. + + Returns: + Callable: The new handler. + """ + if len(param_names) < 1: + raise TypeError("You must specify at least one required paramater name") + # If function is used as a decorator with no arguments, the first argument will be + # a function, so type check all of the param names to catch mistakes + if not all(isinstance(p, str) for p in param_names): + raise TypeError("All paramater names should be strings.") + + def decorator(func: Callable) -> Callable: + @wraps(func) + def handler(*args: Any, **kwargs: Any) -> flask.Response: + raise NotImplementedError() + + return handler + + return decorator