[python] 함수 호출시 시간 초과

파이썬에서 멈추고 스크립트를 다시 시작하라는 함수를 파이썬에서 호출하고 있습니다.

함수를 어떻게 호출하거나 5 초 이상 걸리면 스크립트가 취소하고 다른 작업을 수행하도록 랩핑하는 방법은 무엇입니까?



답변

UNIX에서 실행중인 경우 신호 패키지를 사용할 수 있습니다 .

In [1]: import signal

# Register an handler for the timeout
In [2]: def handler(signum, frame):
   ...:     print("Forever is over!")
   ...:     raise Exception("end of time")
   ...:

# This function *may* run for an indetermined time...
In [3]: def loop_forever():
   ...:     import time
   ...:     while 1:
   ...:         print("sec")
   ...:         time.sleep(1)
   ...:
   ...:

# Register the signal function handler
In [4]: signal.signal(signal.SIGALRM, handler)
Out[4]: 0

# Define a timeout for your function
In [5]: signal.alarm(10)
Out[5]: 0

In [6]: try:
   ...:     loop_forever()
   ...: except Exception, exc:
   ...:     print(exc)
   ....:
sec
sec
sec
sec
sec
sec
sec
sec
Forever is over!
end of time

# Cancel the timer if the function returned before timeout
# (ok, mine won't but yours maybe will :)
In [7]: signal.alarm(0)
Out[7]: 0

통화 후 10 초 alarm.alarm(10) 핸들러가 호출됩니다. 일반 파이썬 코드에서 가로 챌 수있는 예외가 발생합니다.

이 모듈은 스레드와 잘 작동하지 않습니다 (하지만 누가 사용합니까?)

하는 것으로 타임 아웃이 발생하면 우리는 예외를 발생하기 때문에, 그것은 하나 개의 기능, 예를 들어, 적발 및 함수 내에서 무시 끝낼 수 있습니다 :

def loop_forever():
    while 1:
        print('sec')
        try:
            time.sleep(10)
        except:
            continue


답변

당신이 사용할 수있는 multiprocessing.Process정확히 그렇게 .

암호

import multiprocessing
import time

# bar
def bar():
    for i in range(100):
        print "Tick"
        time.sleep(1)

if __name__ == '__main__':
    # Start bar as a process
    p = multiprocessing.Process(target=bar)
    p.start()

    # Wait for 10 seconds or until process finishes
    p.join(10)

    # If thread is still active
    if p.is_alive():
        print "running... let's kill it..."

        # Terminate
        p.terminate()
        p.join()


답변

함수를 어떻게 호출하거나 5 초 이상 걸리면 스크립트가 취소하도록 랩핑하는 방법은 무엇입니까?

나는 이 질문 / 문제를 해결 하는 요점 을 데코레이터와threading.Timer . 여기에 고장이 있습니다.

호환성을위한 가져 오기 및 설정

Python 2 및 3으로 테스트되었으며 Unix / Linux 및 Windows에서도 작동합니다.

먼저 수입품. 다음은 Python 버전에 관계없이 코드 일관성을 유지하려는 시도입니다.

from __future__ import print_function
import sys
import threading
from time import sleep
try:
    import thread
except ImportError:
    import _thread as thread

버전 독립적 코드 사용 :

try:
    range, _print = xrange, print
    def print(*args, **kwargs):
        flush = kwargs.pop('flush', False)
        _print(*args, **kwargs)
        if flush:
            kwargs.get('file', sys.stdout).flush()
except NameError:
    pass

이제 표준 라이브러리에서 기능을 가져 왔습니다.

exit_after 데코레이터

다음으로 main()자식 스레드에서 종료하는 함수가 필요합니다 .

def quit_function(fn_name):
    # print to stderr, unbuffered in Python 2.
    print('{0} took too long'.format(fn_name), file=sys.stderr)
    sys.stderr.flush() # Python 3 stderr is likely buffered.
    thread.interrupt_main() # raises KeyboardInterrupt

그리고 여기 데코레이터 자체가 있습니다.

def exit_after(s):
    '''
    use as decorator to exit process if
    function takes longer than s seconds
    '''
    def outer(fn):
        def inner(*args, **kwargs):
            timer = threading.Timer(s, quit_function, args=[fn.__name__])
            timer.start()
            try:
                result = fn(*args, **kwargs)
            finally:
                timer.cancel()
            return result
        return inner
    return outer

용법

다음은 5 초 후에 종료에 대한 귀하의 질문에 직접 답변하는 사용법입니다!

@exit_after(5)
def countdown(n):
    print('countdown started', flush=True)
    for i in range(n, -1, -1):
        print(i, end=', ', flush=True)
        sleep(1)
    print('countdown finished')

데모:

>>> countdown(3)
countdown started
3, 2, 1, 0, countdown finished
>>> countdown(10)
countdown started
10, 9, 8, 7, 6, countdown took too long
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 11, in inner
  File "<stdin>", line 6, in countdown
KeyboardInterrupt

두 번째 함수 호출이 완료되지 않고 대신 프로세스가 역 추적로 종료됩니다!

KeyboardInterrupt 잠자는 실을 항상 멈추지는 않습니다

Windows의 Python 2에서 키보드 인터럽트로 인해 절전 모드가 항상 중단되는 것은 아닙니다.

@exit_after(1)
def sleep10():
    sleep(10)
    print('slept 10 seconds')

>>> sleep10()
sleep10 took too long         # Note that it hangs here about 9 more seconds
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 11, in inner
  File "<stdin>", line 3, in sleep10
KeyboardInterrupt

명시 적으로 확인하지 않는 한 확장에서 실행되는 코드를 중단하지 않을 수도 PyErr_CheckSignals()있습니다. Cython, Python 및 KeyboardInterrupt를 참조하십시오.

어쨌든 스레드가 1 초 이상 잠자 지 않도록해야합니다. 프로세서 시간이 짧습니다.

함수를 어떻게 호출합니까? 또는 5 초 이상 걸리면 스크립트가 취소하고 다른 작업을 수행하기 위해 무엇을 랩핑 합니까?

그것을 잡아서 다른 것을하기 위해 KeyboardInterrupt를 잡을 수 있습니다.

>>> try:
...     countdown(10)
... except KeyboardInterrupt:
...     print('do something else')
...
countdown started
10, 9, 8, 7, 6, countdown took too long
do something else


답변

순수한 함수 (스레딩 제안과 동일한 API 사용)이며 다른 제안이 있습니다 (이 스레드에 대한 제안을 기반으로)

def timeout(func, args=(), kwargs={}, timeout_duration=1, default=None):
    import signal

    class TimeoutError(Exception):
        pass

    def handler(signum, frame):
        raise TimeoutError()

    # set the timeout handler
    signal.signal(signal.SIGALRM, handler)
    signal.alarm(timeout_duration)
    try:
        result = func(*args, **kwargs)
    except TimeoutError as exc:
        result = default
    finally:
        signal.alarm(0)

    return result


답변

단위 테스트에서 시간 초과 호출을 검색 할 때이 스레드를 가로 질러 실행되었습니다. 답변이나 타사 패키지에서 간단한 것을 찾지 못했기 때문에 아래 코드를 넣을 수있는 데코레이터를 작성했습니다.

import multiprocessing.pool
import functools

def timeout(max_timeout):
    """Timeout decorator, parameter in seconds."""
    def timeout_decorator(item):
        """Wrap the original function."""
        @functools.wraps(item)
        def func_wrapper(*args, **kwargs):
            """Closure for function."""
            pool = multiprocessing.pool.ThreadPool(processes=1)
            async_result = pool.apply_async(item, args, kwargs)
            # raises a TimeoutError if execution exceeds max_timeout
            return async_result.get(max_timeout)
        return func_wrapper
    return timeout_decorator

그런 다음 테스트 또는 원하는 기능을 시간 초과하는 것은 간단합니다.

@timeout(5.0)  # if execution takes longer than 5 seconds, raise a TimeoutError
def test_base_regression(self):
    ...


답변

stopitpypi에 있는 패키지는 시간 초과를 잘 처리하는 것 같습니다.

나는 @stopit.threading_timeoutable데코레이터를 좋아하는데, 데코레이터 timeout는 데코 레이팅 된 함수에 매개 변수를 추가 합니다.

pypi에서 확인하십시오. https://pypi.python.org/pypi/stopit


답변

많은 제안이 있지만 동시 처리를 사용하는 것은 없습니다.이를 처리하는 가장 읽기 쉬운 방법이라고 생각합니다.

from concurrent.futures import ProcessPoolExecutor

# Warning: this does not terminate function if timeout
def timeout_five(fnc, *args, **kwargs):
    with ProcessPoolExecutor() as p:
        f = p.submit(fnc, *args, **kwargs)
        return f.result(timeout=5)

읽고 유지하기가 매우 간단합니다.

풀을 만들고 단일 프로세스를 제출 한 다음 필요에 따라 잡을 수있는 TimeoutError를 발생시키기 전에 최대 5 초 동안 기다립니다.

파이썬 3.2 이상을 기본으로하며 2.7 (핍 설치 선물)로 백 포트되었습니다.

스레드와 프로세스간에 전환 교체 한 간단하다 ProcessPoolExecutor으로ThreadPoolExecutor 합니다.

시간 초과시 프로세스를 종료하려면 Pebble을 살펴보십시오 .