docs: def requestargs(*vars, **config):
      """
      Decorator that loads parameters from request.values if not specified in the
      function's keyword arguments. Usage::
  
          @requestargs('param1', ('param2', int), 'param3[]', ...)
          def function(param1, param2=0, param3=None):
              ...
  
      requestargs takes a list of parameters to pass to the wrapped function, with
      an optional filter (useful to convert incoming string request data into integers
      and other common types). If a required parameter is missing and your function does
      not specify a default value, Python will raise TypeError. requestargs recasts this
      as :exc:`RequestTypeError`, which returns HTTP 400 Bad Request.
  
      If the parameter name ends in ``[]``, requestargs will attempt to read a list from
      the incoming data. Filters are applied to each member of the list, not to the whole
      list.
  
      If the filter raises a ValueError, this is recast as a :exc:`RequestValueError`,
      which also returns HTTP 400 Bad Request.
  
      Tests::
  
          >>> from flask import Flask
          >>> app = Flask(__name__)
          >>>
          >>> @requestargs('p1', ('p2', int), ('p3[]', int))
          ... def f(p1, p2=None, p3=None):
          ...     return p1, p2, p3
          ...
          >>> f(p1=1)
          (1, None, None)
          >>> f(p1=1, p2=2)
          (1, 2, None)
          >>> f(p1='a', p2='b')
          ('a', 'b', None)
          >>> with app.test_request_context('/?p2=2'):
          ...     f(p1='1')
          ...
          ('1', 2, None)
          >>> with app.test_request_context('/?p3=1&p3=2'):
          ...     f(p1='1', p2='2')
          ...
          ('1', '2', [1, 2])
          >>> with app.test_request_context('/?p2=100&p3=1&p3=2'):
          ...     f(p1='1', p2=200)
          ...
          ('1', 200, [1, 2])
      """
      if config and list(config.keys()) != ['source']:
          raise TypeError("Unrecognised parameters: %s" % repr(config.keys()))
  
      def inner(f):
          namefilt = [(name[:-2], filt, True) if name.endswith('[]') else (name, filt, False)
              for name, filt in
                  [(v[0], v[1]) if isinstance(v, (list, tuple)) else (v, None) for v in vars]]
  
          if config and config.get('source') == 'form':
              def datasource():
                  return request.form if request else {}
          elif config and config.get('source') == 'query':
              def datasource():
                  return request.args if request else {}
          else:
              def datasource():
                  return request.values if request else {}
  
          @wraps(f)
          def decorated_function(*args, **kw):
              values = datasource()
              for name, filt, is_list in namefilt:
                  # Process name if
                  # (a) it's not in the function's parameters, and
                  # (b) is in the form/query
                  if name not in kw and name in values:
                      try:
                          if is_list:
                              kw[name] = values.getlist(name, type=filt)
                          else:
                              kw[name] = values.get(name, type=filt)
                      except ValueError as e:
                          raise RequestValueError(e)
              try:
                  return f(*args, **kw)
              except TypeError as e:
                  raise RequestTypeError(e)
          return decorated_function
      return inner