एक फ़ंक्शन कॉल पर टाइमआउट


300

मैं पायथन में एक समारोह बुला रहा हूं, जो मुझे पता है कि स्टाल और मुझे स्क्रिप्ट को पुनरारंभ करने के लिए मजबूर कर सकता है।

मैं फ़ंक्शन को कैसे कॉल कर सकता हूं या मैं इसे क्या लपेटता हूं ताकि अगर यह 5 सेकंड से अधिक समय लगे तो स्क्रिप्ट इसे रद्द कर देती है और कुछ और करती है?

जवाबों:


227

यदि आप 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

5
मैं पायथन 2.5.4 का उपयोग करता हूं। इस तरह की एक त्रुटि है: ट्रेसबैक (सबसे हालिया कॉल अंतिम): फाइल "एएएक्स्ट", लाइन 85, फंक सिग्नल में। सिग्नल (सिग्नल.आईजीआरएलआरएम, हैंडलर) एट्रीब्यूटर: 'मॉड्यूल' ऑब्जेक्ट में कोई विशेषता नहीं है 'एसआईजीएएलआरएम'
फ्लाईपेन

11
@flypen ऐसा इसलिए है क्योंकि signal.alarmऔर संबंधित SIGALRMविंडोज प्लेटफॉर्म पर उपलब्ध नहीं हैं।
डबल AA

2
यदि बहुत सारी प्रक्रियाएं हैं, और प्रत्येक कॉल signal.signal--- क्या वे सभी ठीक से काम करेंगे? क्या प्रत्येक signal.signalकॉल "समवर्ती" को रद्द नहीं करेगा ?
ब्राउनियन

1
सी एक्सटेंशन के साथ इसका उपयोग करने के इच्छुक लोगों के लिए चेतावनी: पायथन सिग्नल हैंडलर को तब तक नहीं बुलाया जाएगा जब तक कि सी फ़ंक्शन पायथन दुभाषिया पर नियंत्रण वापस नहीं करता। इस उपयोग के मामले के लिए, ATOzTOA के उत्तर का उपयोग करें: stackoverflow.com/a/14924210/1286628
wkschwartz

13
मैंने थ्रेड्स के बारे में दूसरी चेतावनी दी है। signal.alarm केवल मुख्य धागे पर काम करता है। मैंने इसे Django के दृश्यों में उपयोग करने की कोशिश की - केवल मुख्य धागे के बारे में क्रिया के साथ तत्काल असफल।
JL Peyret

154

आप 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()

36
मैं लक्ष्य विधि का रिटर्न मान कैसे प्राप्त कर सकता हूं?
bad_keypoint

4
यह काम नहीं करता है अगर कहा जाता है कि फ़ंक्शन I / O ब्लॉक पर अटक गया है।
सूडो

4
@bad_keypoint इस उत्तर को देखें: stackoverflow.com/a/10415215/1384471 मूल रूप से, आप एक सूची पास करते हैं जिसके साथ आप उत्तर देते हैं।
पीटर

1
@ सोडो फिर हटा दें join()। इससे आपके समवर्ती उपप्रकारों की संख्या x के बराबर हो जाती है, जिससे वे अपना काम पूरा कर लेते हैं, या राशि को परिभाषित नहीं किया जाता है join(10)। यदि आपके पास 10 प्रक्रियाओं के लिए एक अवरुद्ध I / O है, तो शामिल होने (10) का उपयोग करके आपने उन्हें उन सभी के लिए अधिकतम 10 प्रतीक्षा करने के लिए सेट किया है जो कि शुरू हो चुकी हैं। इस उदाहरण जैसी डेमॉन फ्लैग का उपयोग करें stackoverflow.com/a/27420072/2480481 । बेशक यू daemon=Trueसीधे multiprocessing.Process()कार्य करने के लिए झंडा पास कर सकता है।
m3nda

2
कम से कम मेरे उद्देश्यों के लिए इस समाधान के साथ समस्या @ATOzTOA, यह है कि यह संभावित रूप से बच्चों को खुद के बाद साफ करने की अनुमति नहीं देता है। समाप्त समारोह के प्रलेखन सेterminate() ... Note that exit handlers and finally clauses, etc., will not be executed. Note that descendant processes of the process will not be terminated – they will simply become orphaned.
abalcerek

78

मैं फ़ंक्शन को कैसे कॉल कर सकता हूं या मैं इसे क्या लपेटता हूं ताकि अगर यह 5 सेकंड से अधिक समय लगे तो स्क्रिप्ट इसे रद्द कर देती है?

मैंने एक जिस्ट पोस्ट किया है जो एक डेकोरेटर और ए के साथ इस प्रश्न / समस्या को हल करता है threading.Timer। यहाँ यह एक टूटने के साथ है।

संगतता के लिए आयात और सेटअप

यह पायथन 2 और 3 के साथ परीक्षण किया गया था। यह यूनिक्स / लिनक्स और विंडोज के तहत भी काम करना चाहिए।

पहले आयात। पायथन संस्करण की परवाह किए बिना कोड को सुसंगत रखने का ये प्रयास:

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 हमेशा एक सोने के धागे को नहीं रोकता है

ध्यान दें कि नींद हमेशा कीबोर्ड की रुकावट से बाधित नहीं होगी, जैसे कि विंडोज पर पायथन 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(), देखें साइथन, पायथन और कीबोर्डइंटरटेनमेंट

मैं किसी भी मामले में, एक से अधिक थ्रेड सोने से बचता हूं - जो प्रोसेसर समय में एक ईओण है।

मैं फ़ंक्शन को कैसे कॉल कर सकता हूं या मैं इसे क्या लपेटता हूं ताकि अगर यह 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

मैंने आपकी पूरी पोस्ट अभी तक नहीं पढ़ी है, लेकिन मैंने अभी-अभी सोचा कि अगर फ्लश 0 हो तो क्या होगा? कि अगर नीचे दिए गए कथन में गलत के रूप में व्याख्या की जाएगी, है ना?
कोएनराड वैन ड्यूइन

2
मुझे कॉल क्यों करना है thread.interrupt_main(), मैं सीधे अपवाद क्यों नहीं उठा सकता?
अनिर्बान नाग 'तिनतिनमज'

इसके multiprocessing.connection.Clientसाथ लपेटने पर कोई विचार ? - हल करने की कोशिश कर रहा है: stackoverflow.com/questions/57817955/…
wwii

51

मेरे पास एक अलग प्रस्ताव है जो एक शुद्ध कार्य है (थ्रेडिंग सुझाव के समान एपीआई के साथ) और ठीक काम करने के लिए लगता है (इस थ्रेड पर सुझावों के आधार पर)

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

3
आपको मूल सिग्नल हैंडलर को भी पुनर्स्थापित करना चाहिए। देखें stackoverflow.com/questions/492519/…
मार्टिन कोंकनी

9
एक और ध्यान दें: यूनिक्स सिग्नल पद्धति केवल तभी काम करती है जब आप इसे मुख्य धागे में लगा रहे हों। इसे उप-थ्रेड में लागू करना एक अपवाद को फेंकता है और काम नहीं करेगा।
मार्टिन कोनसेनी

12
यह सबसे अच्छा समाधान नहीं है क्योंकि यह केवल लिनक्स पर काम करता है।
अधिकतम

17
मैक्स, सच नहीं है - किसी भी POSIX- अनुरूप यूनिक्स पर काम करता है। मुझे लगता है कि आपकी टिप्पणी अधिक सटीक होनी चाहिए, विंडोज पर काम नहीं करती है।
क्रिस जॉनसन

6
आपको एक खाली हुक्मरान के लिए क्वार्ग स्थापित करने से बचना चाहिए। एक सामान्य पायथन गोटा है कि फ़ंक्शन पर डिफ़ॉल्ट तर्क परस्पर हैं। तो उस शब्दकोश को सभी कॉल में साझा किया जाएगा timeoutNoneफ़ंक्शन की पहली पंक्ति पर, डिफ़ॉल्ट को सेट करना और जोड़ना बेहतर है kwargs = kwargs or {}। आर्गन ठीक है क्योंकि ट्यूपल परस्पर नहीं हैं।
scottmrogowski

31

यूनिट परीक्षणों पर टाइमआउट कॉल की खोज करते समय मैं इस धागे के पार गया। मुझे उत्तर या तीसरे पक्ष के पैकेज में कुछ भी सरल नहीं मिला, इसलिए मैंने नीचे दिए गए डेकोरेटर को लिखा था कि आप सही कोड में छोड़ सकते हैं:

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):
    ...

14
सावधान रहें क्योंकि यह समय समाप्त होने के बाद फ़ंक्शन को समाप्त नहीं करता है!
सिल्वेन

ध्यान दें कि विंडोज पर, यह एक पूरी तरह से नई प्रक्रिया को जन्म देता है - जो समय-समय पर खाएगा, शायद बहुत कुछ अगर निर्भरता को स्थापित करने में लंबा समय लगता है।
हारून हॉल

1
हां, इसके लिए कुछ ट्विकिंग की जरूरत है। यह हमेशा के लिए जा रहे धागे छोड़ देता है।
सूडो २

2
IDK अगर यह सबसे अच्छा तरीका है, लेकिन आप Exceptionfunc_wrapper के अंदर कोशिश कर सकते हैं / पकड़ सकते हैं और यह pool.close()सुनिश्चित करने के लिए कि थ्रेड हमेशा मर जाता है तो कोई फर्क नहीं पड़ता। तब आप फेंक सकते हैं TimeoutErrorया जो भी आप चाहते हैं। लगता है मेरे लिए काम करता है।
sudo

2
यह उपयोगी है, लेकिन एक बार मैंने इसे बहुत बार किया है, मुझे मिलता है RuntimeError: can't start new thread। क्या यह अभी भी काम करेगा अगर मैं इसे अनदेखा करता हूं या कुछ और है जो मैं इसे पाने के लिए कर सकता हूं? अग्रिम में धन्यवाद!
बेन्जी

19

stopitपैकेज, pypi पर पाया, अच्छी तरह से समय समाप्ति को संभालने के लिए लगता है।

मुझे @stopit.threading_timeoutableडेकोरेटर पसंद है , जो timeoutसजाए गए फ़ंक्शन के लिए एक पैरामीटर जोड़ता है , जो कि आप क्या उम्मीद करते हैं, यह फ़ंक्शन को रोक देता है।

इसे pypi: https://pypi.python.org/pypi/stopit पर देखें


1
यह बहुत काम और धागा-सुरक्षित है! धन्यवाद और प्लस एक! यह सबसे अच्छा विकल्प है जो मुझे अब तक मिला है और स्वीकृत उत्तर से भी बेहतर है !!
याह्या

लाइब्रेरी का दावा है, विंडोज में कुछ कार्यक्षमता काम नहीं करती है।
स्टीफन सिमिक

16

बहुत सारे सुझाव हैं, लेकिन समवर्ती का उपयोग करके कोई नहीं। फ़्यूचर, जो मुझे लगता है कि इसे संभालने का सबसे सुगम तरीका है।

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)

सुपर सरल पढ़ने और बनाए रखने के लिए।

हम एक पूल बनाते हैं, एक एकल प्रक्रिया सबमिट करते हैं और फिर टाइमआउट ईसर को बढ़ाने से पहले 5 सेकंड तक प्रतीक्षा करते हैं जिसे आप पकड़ सकते हैं और फिर भी संभाल सकते हैं।

पाइथन 3.2+ के मूल निवासी और 2.7 के पीछे (पिप इंस्टॉल फ्यूचर्स)।

थ्रेड्स और प्रक्रियाओं के बीच स्विच की जगह के रूप में सरल रूप में है ProcessPoolExecutorके साथThreadPoolExecutor

यदि आप प्रक्रिया को समय समाप्त पर समाप्त करना चाहते हैं तो मैं कंकड़ में देखने का सुझाव दूंगा


2
क्या "चेतावनी: यह समय समाप्त होने पर फ़ंक्शन को समाप्त नहीं करता है"?
स्कॉट स्टैफ़ोर्ड

5
@ScottStafford प्रक्रियाओं / थ्रेड्स केवल इसलिए समाप्त नहीं होती हैं क्योंकि एक टाइमआउट ईसर को उठाया गया है। तो प्रक्रिया या धागा अभी भी पूरा करने के लिए चलाने की कोशिश करेगा और स्वचालित रूप से आपको अपने समय पर वापस नियंत्रण नहीं देगा।
ब्रायन

क्या यह मुझे उस समय मध्यवर्ती होने वाले किसी भी परिणाम को बचाने देगा? उदाहरण के लिए यदि मेरे पास पुनरावर्ती कार्य है जो मैंने 5 से टाइमआउट सेट किया है, और उस समय में मेरे पास आंशिक परिणाम हैं, तो मैं टाइमआउट पर आंशिक परिणाम वापस करने के लिए फ़ंक्शन कैसे लिखूं?
सुमिरोनटन

मैं बिल्कुल इसका उपयोग कर रहा हूं, हालांकि मेरे पास 1000 कार्य हैं, प्रत्येक को टाइमआउट से 5 सेकंड पहले अनुमति दी जाती है। मेरी समस्या यह है कि कोर ऐसे कामों में उलझ जाते हैं जो कभी खत्म नहीं होते हैं, केवल टाइमआउट केवल उन कार्यों पर लागू होता है जो व्यक्तिगत कार्यों पर नहीं होते हैं। समसामयिकी .futures इस afaik का समाधान प्रदान नहीं करता है।
बस्तियान

11

महान, उपयोग करने में आसान और विश्वसनीय PyPi प्रोजेक्ट टाइम -डेकोरेटर ( https://pypi.org/project/timeout-decorator/ )

स्थापना :

pip install timeout-decorator

उपयोग :

import time
import timeout_decorator

@timeout_decorator.timeout(5)
def mytest():
    print "Start"
    for i in range(1,10):
        time.sleep(1)
        print "%d seconds have passed" % i

if __name__ == '__main__':
    mytest()

2
मैं स्पष्ट समाधान की सराहना करता हूं। लेकिन क्या कोई समझा सकता है कि यह पुस्तकालय कैसे काम करता है, खासकर जब मल्टीथ्रेडिंग से निपटना। व्यक्तिगत रूप से मैं एक अज्ञात machanism का उपयोग करने के लिए धागे या संकेतों को संभालने से डरता हूं।
wsysuper

@wsysuper में काम करने के 2 तरीके हैं: नया धागा खोलना या एक नया उपप्रकार (जो धागा सुरक्षित होना चाहिए)
Gil

6

मैं wrapt_timeout_decorator का लेखक हूं

यहां प्रस्तुत अधिकांश समाधान पहली नज़र में लिनक्स के तहत बहुत ही शानदार ढंग से काम करते हैं - क्योंकि हमारे पास कांटा () और सिग्नल () हैं - लेकिन खिड़कियों पर चीजें कुछ अलग दिखती हैं। और जब लिनक्स पर सबथ्रेड्स की बात आती है, तो आप सिग्नल का उपयोग नहीं कर सकते।

विंडोज के तहत एक प्रक्रिया को स्पॉन करने के लिए, इसे लेने योग्य होना चाहिए - और कई सजाए गए फ़ंक्शन या क्लास तरीके नहीं हैं।

तो आपको डिल और मल्टीप्रोसेस (अचार और मल्टीप्रोसेसिंग नहीं) जैसे बेहतर पिकर का उपयोग करने की आवश्यकता है - यही कारण है कि आप प्रोसेपूल एक्सिक्यूटर (या केवल सीमित कार्यक्षमता के साथ) का उपयोग नहीं कर सकते।

टाइमआउट के लिए - आपको यह परिभाषित करने की आवश्यकता है कि टाइमआउट का क्या मतलब है - क्योंकि विंडोज पर प्रक्रिया को स्पॉन करने में काफी (और निश्चित नहीं) समय लगेगा। यह कम समय पर मुश्किल हो सकता है। मान लेते हैं, इस प्रक्रिया को करने में लगभग 0.5 सेकंड (आसानी से !!!) लगते हैं। यदि आप 0.2 सेकंड का टाइमआउट देते हैं तो क्या होना चाहिए? फ़ंक्शन का समय 0.5 + 0.2 सेकंड के बाद होना चाहिए (इसलिए 0.2 सेकंड के लिए विधि चलने दें)? या 0.2 सेकंड के बाद कहा जाना चाहिए प्रक्रिया समय (उस मामले में, सजाया गया कार्य हमेशा समय समाप्त हो जाएगा, क्योंकि उस समय में यह भी नहीं है)?

नेस्टेड डेकोरेटर्स भी नॉटी हो सकते हैं और आप सबथ्रेड में सिग्नल का इस्तेमाल नहीं कर सकते। यदि आप वास्तव में सार्वभौमिक, क्रॉस-प्लेटफ़ॉर्म डेकोरेटर बनाना चाहते हैं, तो यह सब ध्यान में रखा जाना चाहिए (और परीक्षण)।

अन्य समस्याएँ कॉल करने वाले पर वापस जा रही हैं, साथ ही लॉगिंग समस्याएँ भी हैं (यदि सजाया गया फ़ंक्शन में उपयोग किया जाता है - किसी अन्य प्रक्रिया में फ़ाइलों में लॉगिंग समर्थित नहीं है)

मैंने सभी किनारे के मामलों को कवर करने की कोशिश की, आप पैकेज wrapt_timeout_decorator में देख सकते हैं, या कम से कम अपने स्वयं के समाधानों का परीक्षण कर सकते हैं जो वहां इस्तेमाल किए गए unittests से प्रेरित हैं।

@ एलेक्सिस एगर्मोंट - दुर्भाग्य से मेरे पास टिप्पणी करने के लिए पर्याप्त बिंदु नहीं हैं - शायद कोई और आपको सूचित कर सकता है - मुझे लगता है कि मैंने आपका आयात मुद्दा हल कर दिया है।


3

timeout-decoratorविंडोज़ सिस्टम पर काम न करें, विंडोज़ ने अच्छा समर्थन नहीं किया signal

यदि आप विंडोज़ सिस्टम में टाइमआउट-डेकोरेटर का उपयोग करते हैं तो आपको निम्नलिखित मिलेगा

AttributeError: module 'signal' has no attribute 'SIGALRM'

कुछ ने उपयोग करने का सुझाव दिया use_signals=Falseलेकिन मेरे लिए काम नहीं किया।

लेखक @bitranox ने निम्नलिखित पैकेज बनाया:

pip install https://github.com/bitranox/wrapt-timeout-decorator/archive/master.zip

कोड नमूना:

import time
from wrapt_timeout_decorator import *

@timeout(5)
def mytest(message):
    print(message)
    for i in range(1,10):
        time.sleep(1)
        print('{} seconds have passed'.format(i))

def main():
    mytest('starting')


if __name__ == '__main__':
    main()

निम्नलिखित अपवाद देता है:

TimeoutError: Function mytest timed out after 5 seconds

यह एक बहुत अच्छा समाधान की तरह लग रहा है। अजीब तरह से, लाइन from wrapt_timeout_decorator import * मेरे कुछ अन्य आयातों को मारने के लिए लगता है। उदाहरण के लिए, मुझे मिलता है ModuleNotFoundError: No module named 'google.appengine', लेकिन मुझे यह त्रुटि नहीं मिलती है अगर मैं wrapt_timeout_decorator आयात नहीं करता हूं
एलेक्सिस

@AlexisEggermont मैं appengine के साथ इस का उपयोग करने के बारे में था ... तो मैं बहुत उत्सुक ifthis त्रुटि बनी हुई है?
पास्कलवीकूटेन

2

हम उसी के लिए संकेतों का उपयोग कर सकते हैं। मुझे लगता है कि नीचे दिया गया उदाहरण आपके लिए उपयोगी होगा। यह थ्रेड्स की तुलना में बहुत सरल है।

import signal

def timeout(signum, frame):
    raise myException

#this is an infinite loop, never ending under normal circumstances
def main():
    print 'Starting Main ',
    while 1:
        print 'in main ',

#SIGALRM is only usable on a unix platform
signal.signal(signal.SIGALRM, timeout)

#change 5 to however many seconds you need
signal.alarm(5)

try:
    main()
except myException:
    print "whoops"

1
एक विशिष्ट अपवाद चुनना और केवल इसे पकड़ना बेहतर होगा। नंगे try: ... except: ...हमेशा एक बुरा विचार हैं।
१६:२३

मैं आपसे सहमत हूँ।
एआर

2
#!/usr/bin/python2
import sys, subprocess, threading
proc = subprocess.Popen(sys.argv[2:])
timer = threading.Timer(float(sys.argv[1]), proc.terminate)
timer.start()
proc.wait()
timer.cancel()
exit(proc.returncode)

7
हालांकि यह कोड प्रश्न का उत्तर दे सकता है, लेकिन समस्या का हल कैसे और / या इसके संबंध में अतिरिक्त संदर्भ प्रदान करता है, इससे उत्तर के दीर्घकालिक मूल्य में सुधार होगा
Dan Cornilescu

1

मुझे इसकी आवश्यकता थी nestable समय बीच में आता है (SIGALARM ऐसा नहीं कर सकते हैं) कि time.sleep द्वारा अवरुद्ध नहीं किया जाएगा (जो धागा आधारित दृष्टिकोण नहीं कर सकते)। मैंने यहां से कोडिंग और हल्के से संशोधित कोड को समाप्त कर दिया है: http://code.activestate.com/recipes/577600-queue-for-managing-multiple-sigalrm-alarms-concurr/

कोड ही:

#!/usr/bin/python

# lightly modified version of http://code.activestate.com/recipes/577600-queue-for-managing-multiple-sigalrm-alarms-concurr/


"""alarm.py: Permits multiple SIGALRM events to be queued.

Uses a `heapq` to store the objects to be called when an alarm signal is
raised, so that the next alarm is always at the top of the heap.
"""

import heapq
import signal
from time import time

__version__ = '$Revision: 2539 $'.split()[1]

alarmlist = []

__new_alarm = lambda t, f, a, k: (t + time(), f, a, k)
__next_alarm = lambda: int(round(alarmlist[0][0] - time())) if alarmlist else None
__set_alarm = lambda: signal.alarm(max(__next_alarm(), 1))


class TimeoutError(Exception):
    def __init__(self, message, id_=None):
        self.message = message
        self.id_ = id_


class Timeout:
    ''' id_ allows for nested timeouts. '''
    def __init__(self, id_=None, seconds=1, error_message='Timeout'):
        self.seconds = seconds
        self.error_message = error_message
        self.id_ = id_
    def handle_timeout(self):
        raise TimeoutError(self.error_message, self.id_)
    def __enter__(self):
        self.this_alarm = alarm(self.seconds, self.handle_timeout)
    def __exit__(self, type, value, traceback):
        try:
            cancel(self.this_alarm) 
        except ValueError:
            pass


def __clear_alarm():
    """Clear an existing alarm.

    If the alarm signal was set to a callable other than our own, queue the
    previous alarm settings.
    """
    oldsec = signal.alarm(0)
    oldfunc = signal.signal(signal.SIGALRM, __alarm_handler)
    if oldsec > 0 and oldfunc != __alarm_handler:
        heapq.heappush(alarmlist, (__new_alarm(oldsec, oldfunc, [], {})))


def __alarm_handler(*zargs):
    """Handle an alarm by calling any due heap entries and resetting the alarm.

    Note that multiple heap entries might get called, especially if calling an
    entry takes a lot of time.
    """
    try:
        nextt = __next_alarm()
        while nextt is not None and nextt <= 0:
            (tm, func, args, keys) = heapq.heappop(alarmlist)
            func(*args, **keys)
            nextt = __next_alarm()
    finally:
        if alarmlist: __set_alarm()


def alarm(sec, func, *args, **keys):
    """Set an alarm.

    When the alarm is raised in `sec` seconds, the handler will call `func`,
    passing `args` and `keys`. Return the heap entry (which is just a big
    tuple), so that it can be cancelled by calling `cancel()`.
    """
    __clear_alarm()
    try:
        newalarm = __new_alarm(sec, func, args, keys)
        heapq.heappush(alarmlist, newalarm)
        return newalarm
    finally:
        __set_alarm()


def cancel(alarm):
    """Cancel an alarm by passing the heap entry returned by `alarm()`.

    It is an error to try to cancel an alarm which has already occurred.
    """
    __clear_alarm()
    try:
        alarmlist.remove(alarm)
        heapq.heapify(alarmlist)
    finally:
        if alarmlist: __set_alarm()

और एक उपयोग उदाहरण:

import alarm
from time import sleep

try:
    with alarm.Timeout(id_='a', seconds=5):
        try:
            with alarm.Timeout(id_='b', seconds=2):
                sleep(3)
        except alarm.TimeoutError as e:
            print 'raised', e.id_
        sleep(30)
except alarm.TimeoutError as e:
    print 'raised', e.id_
else:
    print 'nope.'

यह भी संकेत का उपयोग करता है इसलिए काम नहीं करेगा यदि एक थ्रेड से कहा जाता है।

0

यहां दिए गए थ्रेड-आधारित समाधान में थोड़ा सुधार है।

नीचे दिए गए कोड अपवादों का समर्थन करते हैं :

def runFunctionCatchExceptions(func, *args, **kwargs):
    try:
        result = func(*args, **kwargs)
    except Exception, message:
        return ["exception", message]

    return ["RESULT", result]


def runFunctionWithTimeout(func, args=(), kwargs={}, timeout_duration=10, default=None):
    import threading
    class InterruptableThread(threading.Thread):
        def __init__(self):
            threading.Thread.__init__(self)
            self.result = default
        def run(self):
            self.result = runFunctionCatchExceptions(func, *args, **kwargs)
    it = InterruptableThread()
    it.start()
    it.join(timeout_duration)
    if it.isAlive():
        return default

    if it.result[0] == "exception":
        raise it.result[1]

    return it.result[1]

5 सेकंड के समय के साथ इसे लागू करना:

result = timeout(remote_calculate, (myarg,), timeout_duration=5)

1
यह मूल ट्रेसबैक को छिपाते हुए एक नया अपवाद देगा। नीचे मेरा संस्करण देखें ...
मीथम

1
यह भी असुरक्षित है, जैसे कि कुछ runFunctionCatchExceptions()निश्चित कार्यों के भीतर GIL प्राप्त करने वाले कार्य कहलाते हैं। उदाहरण के लिए निम्नलिखित कभी नहीं, या बहुत लंबे समय के लिए, अगर समारोह के भीतर बुलाया जाता है eval(2**9999999999**9999999999):। देखें stackoverflow.com/questions/22138190/...
मिक्को Ohtamaa
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.