hooks.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. from typing import Any, Callable, Dict, List
  2. def before_request(
  3. hook: Callable[[], Any] = None, only: List[str] = None
  4. ) -> Callable[..., Any]:
  5. """
  6. This decorator provides a way to hook into the request
  7. lifecycle by enqueueing methods to be invoked before
  8. each handler in the view. If the method returns a value
  9. other than :code:`None`, then that value will be returned
  10. to the client. If invoked with the :code:`only` kwarg,
  11. the hook will only be invoked for the given list of
  12. handler methods.
  13. Examples::
  14. class MyFeature(ModelView)
  15. @before_request
  16. def ensure_feature_is_enabled(self):
  17. if self.feature_is_disabled:
  18. return self.response_404()
  19. return None
  20. # etc...
  21. class MyView(ModelRestAPI):
  22. @before_request(only=["create", "update", "delete"])
  23. def ensure_write_mode_enabled(self):
  24. if self.read_only:
  25. return self.response_400()
  26. return None
  27. # etc...
  28. :param hook:
  29. A callable to be invoked before handlers in the class. If the
  30. hook returns :code:`None`, then the request proceeds and the
  31. handler is invoked. If it returns something other than :code:`None`,
  32. then execution halts and that value is returned to the client.
  33. :param only:
  34. An optional list of the names of handler methods. If present,
  35. :code:`hook` will only be invoked before the handlers specified
  36. in the list. If absent, :code:`hook` will be invoked for before
  37. all handlers in the class.
  38. """
  39. def wrap(hook: Callable[[], Any]) -> Callable[[], Any]:
  40. hook._before_request_hook = True
  41. hook._before_request_only = only
  42. return hook
  43. return wrap if hook is None else wrap(hook)
  44. def wrap_route_handler_with_hooks(
  45. handler_name: str,
  46. handler: Callable[..., Any],
  47. before_request_hooks: List[Callable[[], Any]],
  48. ) -> Callable[..., Any]:
  49. applicable_hooks = []
  50. for hook in before_request_hooks:
  51. only = hook._before_request_only
  52. applicable_hook = only is None or handler_name in only
  53. if applicable_hook:
  54. applicable_hooks.append(hook)
  55. if not applicable_hooks:
  56. return handler
  57. def wrapped_handler(*args: List[Any], **kwargs: Dict[str, Any]) -> Any:
  58. for hook in applicable_hooks:
  59. result = hook()
  60. if result is not None:
  61. return result
  62. return handler(*args, **kwargs)
  63. return wrapped_handler
  64. def get_before_request_hooks(view_or_api_instance: Any) -> List[Callable[[], Any]]:
  65. before_request_hooks = []
  66. for attr_name in dir(view_or_api_instance):
  67. attr = getattr(view_or_api_instance, attr_name)
  68. if hasattr(attr, "_before_request_hook") and attr._before_request_hook is True:
  69. before_request_hooks.append(attr)
  70. return before_request_hooks