      def name(self):
          """ Return thread name """
          return self._name
  
      def check_and_raise_error(self):
          """ Checks for errors in thread and raises them in caller """
          if not self.has_error:
              return
          logger.debug("Thread error caught: %s", self.errors)
          error = self.errors[0]
          raise error[1].with_traceback(error[2])
 
</s> remove     def name(self):
        """ Return thread name """
 </s> add     def name(self) -> str:
        """ :str: The name of the thread """ </s> remove     def is_alive(self):
        """ Return true if any thread is alive else false """
 </s> add     def is_alive(self) -> bool:
        """ Check if any threads are still alive

        Returns
        -------
        bool
            ``True`` if any threads are alive. ``False`` if no threads are alive
        """ </s> remove     """ Subclass of thread that passes errors back to parent """
    def __init__(self, group=None, target=None, name=None,  # pylint: disable=too-many-arguments
                 args=(), kwargs=None, *, daemon=None):
        super().__init__(group=group, target=target, name=name,
                         args=args, kwargs=kwargs, daemon=daemon)
        self.err = None

    def check_and_raise_error(self):
        """ Checks for errors in thread and raises them in caller """
 </s> add     """ Subclass of thread that passes errors back to parent

    Parameters
    ----------
    target: callable object, Optional
        The callable object to be invoked by the run() method. If ``None`` nothing is called.
        Default: ``None``
    name: str, optional
        The thread name. if ``None`` a unique name is constructed of the form "Thread-N" where N
        is a small decimal number. Default: ``None``
    args: tuple
        The argument tuple for the target invocation. Default: ().
    kwargs: dict
        keyword arguments for the target invocation. Default: {}.
    """
    _target: Callable
    _args: Tuple
    _kwargs: Dict[str, Any]
    _name: str

    def __init__(self,
                 target: Optional[Callable] = None,
                 name: Optional[str] = None,
                 args: Tuple = (),
                 kwargs: Dict[str, Any] = None,
                 *,
                 daemon: Optional[bool] = None) -> None:
        super().__init__(target=target, name=name, args=args, kwargs=kwargs, daemon=daemon)
        self.err: _ErrorType = None

    def check_and_raise_error(self) -> None:
        """ Checks for errors in thread and raises them in caller.

        Raises
        ------
        Error
            Re-raised error from within the thread
        """ </s> remove     def errors(self):
        """ Return a list of thread errors """
 </s> add     def errors(self) -> List[_ErrorType]:
        """ list: List of thread error values """
    def check_and_raise_error(self) -> None:
        """ Checks for errors in thread and raises them in caller.

        Raises
        ------
        Error
            Re-raised error from within the thread
        """
      def name(self):
          """ Return thread name """
          return self._name
  
    def check_and_raise_error(self) -> None:
        """ Checks for errors in thread and raises them in caller.

        Raises
        ------
        Error
            Re-raised error from within the thread
        """
    def check_and_raise_error(self) -> None:
        """ Checks for errors in thread and raises them in caller.

        Raises
        ------
        Error
            Re-raised error from within the thread
        """
          if not self.has_error:
              return
          logger.debug("Thread error caught: %s", self.errors)
          error = self.errors[0]
          raise error[1].with_traceback(error[2])
keep keep keep keep replace replace keep keep keep keep keep
 bugfix - timelapse image loader
multithreading.py - typing + docs 
lib/multithreading.py