'Why can't I interrupt a generator with timeout in python3?

I have written a generator as follows:

def my_generator():
    i = 0
    while i < 1000000:
        i += 1
        yield i

Assuming the generator cannot be executed in a second and in the test function I use a timeout decorator to guarantee the function should not run more than 1 second.

@timeout(1)
def test():
    for i in my_generator:
        print(i)  

Unfortunately, the timeout don't work as I wanted, the function print all the number from 1 to 1000000 with more than 1 second. In the decorator, I have tried gevent and KThread, but none of them can work. Decorator using KThread:

class KThread(threading.Thread):
    """Subclass of threading.Thread, with a kill() method."""
    def __init__(self, *args, **kwargs):
        threading.Thread.__init__(self, *args, **kwargs)
        self.killed = False
    def start(self):
        """Start the thread."""
        self.__run_backup = self.run
        """Force the Thread to install our trace."""
        self.run = self.__run
        threading.Thread.start(self)
    def __run(self):
        """Hacked run function, which installs the trace."""
        sys.settrace(self.globaltrace)
        self.__run_backup()
        self.run = self.__run_backup
    def globaltrace(self, frame, why, arg):
        if why == 'call':
            return self.localtrace
        else:
            return None
    def localtrace(self, frame, why, arg):
        if self.killed:
            if why == 'line':
                raise SystemExit()
        return self.localtrace
    def kill(self):
        self.killed = True

def timeout(seconds):
    def timeout_decorator(func):
        def _new_func(oldfunc, result, oldfunc_args, oldfunc_kwargs):
            result.append(oldfunc(*oldfunc_args, **oldfunc_kwargs))
        def _(*args, **kwargs):
            result = []
            '''create new args for _new_funcbecause
               we want to get the func return val to result list
            '''
            new_kwargs = {
                'oldfunc': func,
                'result': result,
                'oldfunc_args': args,
                'oldfunc_kwargs': kwargs
            }
            thd = KThread(target=_new_func, args=(), kwargs=new_kwargs)
            thd.start()
            thd.join(seconds)
            alive = thd.isAlive()
            '''kill the child thread'''
            thd.kill()
            if alive:
                alert_exce = u'function timeout for [%d s].' % seconds
                raise Timeout(alert_exce)
            else:
                return result[0]
        _.__name__ = func.__name__
        _.__doc__ = func.__doc__
        return _
    return timeout_decorator

Decorator using gevent:

def g_timer(timeout_seconds=None, timeout_exception=TimeoutError, exception_message=None, module_name=None):
    import gevent
    from gevent import monkey
    monkey.patch_all()
    def decorate(func):
        def wrapper(*args, **kwargs):
            try:
                t0 = time.time()
                gevent.with_timeout(timeout_seconds, func, *args, **kwargs)
                elapsed = time.time() - t0
            except gevent.timeout.Timeout as e:
                print("exception")
        return wrapper
    return decorate


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source