code: def timeout(seconds=None, use_signals=True, timeout_exception=TimeoutError, exception_message=None):
      
      def decorate(function):
  
          if not seconds:
              return function
  
          if use_signals:
              def handler(signum, frame):
                  _raise_exception(timeout_exception, exception_message)
  
              @wraps(function)
              def new_function(*args, **kwargs):
                  new_seconds = kwargs.pop('timeout', seconds)
                  if new_seconds:
                      old = signal.signal(signal.SIGALRM, handler)
                      signal.setitimer(signal.ITIMER_REAL, new_seconds)
                  try:
                      return function(*args, **kwargs)
                  finally:
                      if new_seconds:
                          signal.setitimer(signal.ITIMER_REAL, 0)
                          signal.signal(signal.SIGALRM, old)
              return new_function
          else:
              @wraps(function)
              def new_function(*args, **kwargs):
                  timeout_wrapper = _Timeout(function, timeout_exception, exception_message, seconds)
                  return timeout_wrapper(*args, **kwargs)
              return new_function
  
      return decorate
docs: Add a timeout parameter to a function and return it.
  
      :param seconds: optional time limit in seconds or fractions of a second. If None is passed, no timeout is applied.
          This adds some flexibility to the usage: you can disable timing out depending on the settings.
      :type seconds: float
      :param use_signals: flag indicating whether signals should be used for timing function out or the multiprocessing
          When using multiprocessing, timeout granularity is limited to 10ths of a second.
      :type use_signals: bool
  
      :raises: TimeoutError if time limit is reached
  
      It is illegal to pass anything other than a function as the first
      parameter. The function is wrapped and returned to the caller.