      def start(self):
          """ Start a thread with the given method and args """
          logger.debug("Starting thread(s): '%s'", self._name)
          for idx in range(self._thread_count):
              name = "{}_{}".format(self._name, idx)
              logger.debug("Starting thread %s of %s: '%s'",
                           idx + 1, self._thread_count, name)
              thread = FSThread(name=name,
                                target=self._target,
                                args=self._args,
 
</s> remove     def start(self):
        """ Start a thread with the given method and args """
 </s> add     def start(self) -> None:
        """ Start all the threads for the given method, args and kwargs """ </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     def has_error(self):
        """ Return true if a thread has errored, otherwise false """
 </s> add     def has_error(self) -> bool:
        """ bool: ``True`` if a thread has errored, otherwise ``False`` """ </s> remove     def join(self):
 </s> add     def join(self) -> None: </s> remove     """ Threading for IO heavy ops
        Catches errors in thread and rethrows to parent """
    def __init__(self, target, *args, thread_count=1, name=None, **kwargs):
        self._name = name if name else target.__name__
 </s> add     """ Threading for IO heavy ops. Catches errors in thread and rethrows to parent.

    Parameters
    ----------
    target: callable object
        The callable object to be invoked by the run() method.
    args: tuple
        The argument tuple for the target invocation. Default: ().
    thread_count: int, optional
        The number of threads to use. Default: 1
    name: str, optional
        The thread name. if ``None`` a unique name is constructed of the form {target.__name__}_N
        where N is an incrementing integer. Default: ``None``
    kwargs: dict
        keyword arguments for the target invocation. Default: {}.
    """
    def __init__(self,
                 target: Callable,
                 *args,
                 thread_count: int = 1,
                 name: Optional[str] = None,
                 **kwargs) -> None:
        self._name = _get_name(name if name else target.__name__)
            name = self._name if self._thread_count == 1 else f"{self._name}_{idx}"
      def start(self):
          """ Start a thread with the given method and args """
          logger.debug("Starting thread(s): '%s'", self._name)
          for idx in range(self._thread_count):
            name = self._name if self._thread_count == 1 else f"{self._name}_{idx}"
              logger.debug("Starting thread %s of %s: '%s'",
                           idx + 1, self._thread_count, name)
              thread = FSThread(name=name,
                                target=self._target,
                                args=self._args,
keep keep keep keep replace keep keep keep keep keep
 bugfix - timelapse image loader
multithreading.py - typing + docs 
lib/multithreading.py