docs: def return_reply(*types, **options):
      """Decorator for returning replies from request handler methods.
  
      The method being decorated should return an iterable of result
      values. If the first value is 'ok', the decorator will check the
      remaining values against the specified list of types (if any).
      If the first value is 'fail' or 'error', there must be only
      one remaining parameter, and it must be a string describing the
      failure or error  In both cases, the decorator will pack the
      values into a reply message.
  
      Parameters
      ----------
      types : list of kattypes
          The types of the reply message parameters (in order).
  
      Keyword Arguments
      -----------------
      major : int, optional
          Major version of KATCP to use when interpreting types.
          Defaults to latest implemented KATCP version.
  
      Examples
      --------
      >>> class MyDevice(DeviceServer):
      ...     @request(Int())
      ...     @return_reply(Int(), Float())
      ...     def request_myreq(self, req, my_int):
      ...         return ("ok", my_int + 1, my_int * 2.0)
      ...
  
      """
      major = options.pop('major', DEFAULT_KATCP_MAJOR)
      if len(options) > 0:
          raise TypeError('return_reply does not take keyword argument(s) %r.'
                          % options.keys())
  
      # Check that only the last type has multiple=True
      if len(types) > 1:
          for type_ in types[:-1]:
              if type_._multiple:
                  raise TypeError('Only the last parameter type '
                                  'can accept multiple arguments.')
  
      def decorator(handler):
          if not handler.__name__.startswith("request_"):
              raise ValueError("This decorator can only be used on a katcp"
                               " request handler (method name should start"
                               " with 'request_').")
          msgname = convert_method_name('request_', handler.__name__)
  
          @wraps(handler)
          def raw_handler(self, *args):
              reply_args = handler(self, *args)
              if gen.is_future(reply_args):
                  return async_make_reply(msgname, types, reply_args, major)
              else:
                  return make_reply(msgname, types, reply_args, major)
  
  
          # TODO NM 2017-01-12 Consider using the decorator module to create
          # signature preserving decorators that would avoid the need for this
          # trickery
          if not getattr(handler, "_request_decorated", False):
              # We are on the inside.
              # We must preserve the original function parameter names for the
              # request decorator
              raw_handler._orig_argnames = inspect.getargspec(handler)[0]
  
          return raw_handler
  
      return decorator