docs: def spoolable(*, pre_condition=True, body_params=()):
      """
      Decorates a function to make it spoolable using uWSGI, but if no spooling mechanism is available,
      the function is called synchronously. All decorated function arguments must be picklable and
      the first annotated with `Context` will receive an object that defines the current execution state.
      Return values are always ignored and all exceptions are caught in spooled mode.
      :param pre_condition: additional condition needed to use spooler
      :param body_params: parameter names that can have large values and should use spooler body
      """
  
      def decorator(func):
          context_name = None
          keyword_kinds = {inspect.Parameter.POSITIONAL_OR_KEYWORD, inspect.Parameter.KEYWORD_ONLY}
          invalid_body_params = set(body_params)
          for name, parameter in inspect.signature(func).parameters.items():
              if parameter.kind not in keyword_kinds:
                  continue
              if not context_name and parameter.annotation is Context:
                  context_name = name
              elif name in invalid_body_params:
                  invalid_body_params.remove(name)
          if invalid_body_params:
              raise TypeError('Spoolable task body_params must be keyword arguments')
  
          task = Task(func, context_name=context_name, pre_condition=pre_condition, body_params=body_params)
          spooler.register(task)
          return task
  
      return decorator