<commit_before>from .. import log
import contextlib, functools, threading, queue


class Runnable:
    """
    Base class for all objects that contain threads - including threads
    created and started by bp code, and threads started by external libraries.

    You can use a Runnable in two ways:  you can call the methods start, stop,
    and join just like for a thread, or you can use it as a context manager:

    with some_runnable() as runnable:
         do_some_things(runnable)

         # At the end of the block, runnable.stop() is called and then
         # the thread is joined

    """

    timeout = 0.1
    running = True

    def start(self):
        self.running = True

    def stop(self):
        self.running = False

    def join(self):
        """Join this thread, or timeout after self.timeout seconds"""
        pass

    def cleanup(self):
        pass

    def is_alive(self):
        return self.running

    def __enter__(self):
        self.start()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.stop()
        try:
            self.join()
        except Exception as e:
            log.error('Unable to join thread %s: %s', self, e)


class RunnableTarget(Runnable):
    def run(self):
        try:
            self.target()
        finally:
            self.cleanup()

    def target(self):
        """The target code that is actually executed in the run method"""
        pass


class RunnableThread(threading.Thread, RunnableTarget):
    def __init__(self, daemon=True, **kwds):
        super().__init__(daemon=daemon, **kwds)

    def start(self):
        RunnableTarget.start(self)
        threading.Thread.start(self)

    def run(self):
        RunnableTarget.run(self)

    def join(self):
        threading.Thread.join(self, self.timeout)

<commit_msg>Catch and report tracebacks in threads<commit_after>from .. import log
import contextlib, functools, threading, traceback, queue


class Runnable:
    """
    Base class for all objects that contain threads - including threads
    created and started by bp code, and threads started by external libraries.

    You can use a Runnable in two ways:  you can call the methods start, stop,
    and join just like for a thread, or you can use it as a context manager:

    with some_runnable() as runnable:
         do_some_things(runnable)

         # At the end of the block, runnable.stop() is called and then
         # the thread is joined

    """

    timeout = 0.1
    running = True

    def start(self):
        self.running = True

    def stop(self):
        self.running = False

    def join(self):
        """Join this thread, or timeout after self.timeout seconds"""
        pass

    def cleanup(self):
        pass

    def is_alive(self):
        return self.running

    def __enter__(self):
        self.start()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.stop()
        try:
            self.join()
        except Exception as e:
            log.error('Unable to join thread %s: %s', self, e)


class RunnableTarget(Runnable):
    def run(self):
        try:
            self.target()
        except:
            log.error('Exception at %s: \n%s', str(self), traceback.format_tb())
        finally:
            self.cleanup()

    def target(self):
        """The target code that is actually executed in the run method"""
        pass


class RunnableThread(threading.Thread, RunnableTarget):
    def __init__(self, daemon=True, **kwds):
        super().__init__(daemon=daemon, **kwds)

    def start(self):
        RunnableTarget.start(self)
        threading.Thread.start(self)

    def run(self):
        RunnableTarget.run(self)

    def join(self):
        threading.Thread.join(self, self.timeout)


{'fraction_non_alphanumeric': 0.091699604743083, 'fraction_numerical': 0.001054018445322793, 'mean_word_length': 2.269595176571921, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '507053', 'n_tokens_mistral': 1130, 'n_tokens_neox': 1066, 'n_words': 373}