बिना कोशिश के ही अजगर में कीबोर्ड पर कब्जा कर लें


102

क्या पायथन में किसी तरह से KeyboardInterruptसभी कोड को बिना किसी घटना के कैप्चर किया जा सकता है try-except स्टेटमेंट किया जा सके?

मैं ट्रेस यदि उपयोगकर्ता प्रेस के बिना सफाई से बाहर निकलने के लिए चाहते हैं Ctrl+ C

जवाबों:


150

हाँ, आप एक अवरोध हैंडलर मॉड्यूल का उपयोग कर स्थापित कर सकते हैं संकेत है, और एक का उपयोग कर हमेशा के लिए इंतजार threading.Event :

import signal
import sys
import time
import threading

def signal_handler(signal, frame):
    print('You pressed Ctrl+C!')
    sys.exit(0)

signal.signal(signal.SIGINT, signal_handler)
print('Press Ctrl+C')
forever = threading.Event()
forever.wait()

10
ध्यान दें कि सिग्नल मॉड्यूल के साथ कुछ प्लेटफ़ॉर्म-विशिष्ट समस्याएं हैं - इस पोस्टर को प्रभावित नहीं करना चाहिए, लेकिन "विंडोज पर, सिग्नल () केवल SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV या SIGTERM के साथ कहा जा सकता है। एक ValueError। किसी अन्य मामले में उठाया जाएगा। ”
bgporter

7
धागे के साथ भी अच्छी तरह से काम करता है। मुझे आशा है कि आप कभी ऐसा नहीं करेंगे while True: continue। (उस शैली में, while True: passवैसे भी, निरर्थक होगा।) यह बहुत बेकार होगा; कुछ करने की कोशिश करें जैसे while True: time.sleep(60 * 60 * 24)(एक समय में एक दिन के लिए सोना पूरी तरह से मनमाना आंकड़ा है)।
क्रिस मॉर्गन

1
यदि आप क्रिस मॉर्गन के उपयोग के सुझाव का उपयोग कर रहे हैं time(जैसा कि आपको करना चाहिए), मत भूलना import time:)
Seaux

1
कॉलिंग sys.exit (0) मेरे लिए एक SystemExit अपवाद को ट्रिगर करता है। आप इसे अच्छी तरह से काम कर सकते हैं यदि आप इसके साथ संयोजन में इसका उपयोग करते हैं: stackoverflow.com/a/13723190/353094
leetNightshade

2
इसके बजाय आप बार-बार नींद की signal.pause (उपयोग कर सकते हैं)
Croad Langshan

36

यदि आप चाहते हैं कि ट्रेसबैक न दिखाया जाए, तो अपना कोड इस तरह बनाएं:

## all your app logic here
def main():
   ## whatever your app does.


if __name__ == "__main__":
   try:
      main()
   except KeyboardInterrupt:
      # do nothing here
      pass

(हां, मुझे पता है कि यह सीधे सवाल का जवाब नहीं देता है, लेकिन यह वास्तव में स्पष्ट नहीं है कि ब्लॉक को छोड़कर / एक प्रयास की आवश्यकता क्यों आपत्तिजनक है - शायद यह ओपी को कम परेशान करता है)


5
किसी कारण से, यह हमेशा मेरे लिए काम नहीं करता है। signal.signal( signal.SIGINT, lambda s, f : sys.exit(0))हमेशा करता है।
हेल ​​कैनरी

यह हमेशा pygtk जैसी चीजों के साथ काम नहीं करता है जो धागे का उपयोग करते हैं। कभी-कभी ^ सी पूरी प्रक्रिया के बजाय केवल वर्तमान थ्रेड को मार देगा, इसलिए अपवाद केवल उस थ्रेड के माध्यम से प्रचारित करेगा।
सूडो बैश

विशेष रूप से
pygtk के

30

अपने स्वयं के सिग्नल हैंडलर को सेट करने का एक विकल्प अपवाद को पकड़ने के लिए एक संदर्भ-प्रबंधक का उपयोग करना है और इसे अनदेखा करना है:

>>> class CleanExit(object):
...     def __enter__(self):
...             return self
...     def __exit__(self, exc_type, exc_value, exc_tb):
...             if exc_type is KeyboardInterrupt:
...                     return True
...             return exc_type is None
... 
>>> with CleanExit():
...     input()    #just to test it
... 
>>>

यह हटाता है try-except जबकि क्या चल रहा है में से कुछ स्पष्ट उल्लेख संरक्षण ब्लॉक।

यह आपको अपने कोड के कुछ हिस्सों में केवल रुकावट को अनदेखा करने की अनुमति देता है और फिर से सिग्नल हैंडलर को हर बार सेट और रीसेट किए बिना।


1
अच्छा है, यह समाधान संकेतों से निपटने के बजाय उद्देश्य को व्यक्त करने में थोड़ा अधिक प्रत्यक्ष लगता है।
सीयूक्स

मल्टीप्रोसेसिंग लाइब्रेरी का उपयोग करना, मुझे यकीन नहीं है कि मुझे किस ऑब्जेक्ट पर उन तरीकों को जोड़ना चाहिए .. कोई सुराग?
स्टेफेन

@ स्टीफन आपका क्या मतलब है? मल्टीप्रोसेसिंग से निपटने के दौरान आपको माता-पिता और बच्चे दोनों की प्रक्रियाओं में सिग्नल से निपटना होगा, क्योंकि यह दोनों में ट्रिगर हो सकता है। यह वास्तव में इस बात पर निर्भर करता है कि आप क्या कर रहे हैं और आपके सॉफ़्टवेयर का उपयोग कैसे किया जाएगा।
बकुरीउ

8

मुझे पता है कि यह एक पुराना सवाल है लेकिन मैं पहले यहां आया और फिर atexitमॉड्यूल की खोज की । मैं इसके क्रॉस-प्लेटफ़ॉर्म ट्रैक रिकॉर्ड या कैविट्स की पूरी सूची के बारे में अभी तक नहीं जानता, लेकिन अभी तक यह वही है जो मैं KeyboardInterruptलिनक्स पर पोस्ट- क्लीनअप को संभालने की कोशिश में देख रहा था । बस समस्या के करीब पहुंचने का एक और तरीका फेंकना चाहता था।

मैं फैब्रिक ऑपरेशंस के संदर्भ में पोस्ट-एक्जिट क्लीन-अप करना चाहता हूं, इसलिए try/ में सब कुछ लपेटना exceptमेरे लिए एक विकल्प नहीं था। मन करता हैatexit ऐसी स्थिति में एक अच्छा फिट हो सकता है, जहां आपका कोड नियंत्रण प्रवाह के शीर्ष स्तर पर नहीं है।

atexit उदाहरण के लिए, बॉक्स से बहुत सक्षम और पठनीय है:

import atexit

def goodbye():
    print "You are now leaving the Python sector."

atexit.register(goodbye)

आप इसे डेकोरेटर के रूप में भी उपयोग कर सकते हैं (2.6 के रूप में; यह उदाहरण डॉक्स से है):

import atexit

@atexit.register
def goodbye():
    print "You are now leaving the Python sector."

यदि आप इसे KeyboardInterruptकेवल विशिष्ट बनाना चाहते हैं , तो किसी अन्य व्यक्ति का इस प्रश्न का उत्तर संभवतः बेहतर है।

लेकिन ध्यान दें कि atexitमॉड्यूल केवल ~ 70 लाइनों का कोड है और यह एक समान संस्करण बनाने के लिए कठिन नहीं होगा जो अपवादों को अलग तरह से व्यवहार करता है, उदाहरण के लिए कॉलबैक फ़ंक्शन के तर्कों के रूप में अपवादों को पारित करना। (उस की सीमा atexitएक संशोधित संस्करण को वारंट करेगी: वर्तमान में मैं अपवादों के बारे में जानने के लिए एग्ज़िट-कॉलबैक-फ़ंक्शंस के लिए एक तरह से गर्भ धारण नहीं कर सकता; atexitहैंडलर अपवाद को पकड़ता है, आपके कॉलबैक को कॉल करता है), फिर से उठाता है वह अपवाद है। लेकिन आप इसे अलग तरीके से कर सकते हैं।)

अधिक जानकारी के लिए देखें:


Atexit does not 'वर्क फॉर
कीबोर्डइंटरप्ट

यहाँ KeyboardInterrupt के लिए काम किया (अजगर 3.7, MacOS)। हो सकता है कि एक मंच-विशिष्ट quirk?
निको निमन

4

आप ( KeyboardInterruptबिना try: ... except KeyboardInterrupt: pass, सबसे स्पष्ट और पूरी तरह से "सर्वश्रेष्ठ" समाधान के लिए एक स्टैक ट्रेस को प्रिंट करने से रोक सकते हैं, लेकिन आप पहले से ही इसे जानते हैं और कुछ और के लिए कहा है) sys.excepthook। कुछ इस तरह

def custom_excepthook(type, value, traceback):
    if type is KeyboardInterrupt:
        return # do nothing
    else:
        sys.__excepthook__(type, value, traceback)

यदि उपयोगकर्ता ctrl-c
Alex

7
यह बिल्कुल भी सच नहीं है। KeyboardInterrupt अपवाद व्यवधान हैंडलर के दौरान बनाया गया है। SIGINT के लिए डिफॉल्ट हैंडलर KeyboardInterrupt को उठाता है, इसलिए यदि आप नहीं चाहते हैं कि आपको जो व्यवहार करना है, वह SIGINT के लिए एक अलग सिग्नल हैंडलर प्रदान करे। आप इस बात में सही हैं कि अपवादों को केवल एक कोशिश में संभाला जा सकता है / इसके अलावा हालांकि इस मामले में आप अपवाद को पहले स्थान पर उठाए जाने से बचा सकते हैं।
मैट

1
हाँ, मुझे पता चला है कि पोस्ट करने के लगभग तीन मिनट बाद, जब कोटलिंस्की का जवाब लुढ़का;)

2

मैंने सभी द्वारा सुझाए गए समाधानों की कोशिश की, लेकिन मुझे वास्तव में इसे काम करने के लिए खुद को कोड को सुधारना पड़ा। निम्नलिखित मेरा सुधार कोड है:

import signal
import sys
import time

def signal_handler(signal, frame):
    print('You pressed Ctrl+C!')
    print(signal) # Value is 2 for CTRL + C
    print(frame) # Where your execution of program is at moment - the Line Number
    sys.exit(0)

#Assign Handler Function
signal.signal(signal.SIGINT, signal_handler)

# Simple Time Loop of 5 Seconds
secondsCount = 5
print('Press Ctrl+C in next '+str(secondsCount))
timeLoopRun = True 
while timeLoopRun:  
    time.sleep(1)
    if secondsCount < 1:
        timeLoopRun = False
    print('Closing in '+ str(secondsCount)+ ' seconds')
    secondsCount = secondsCount - 1

0

यदि कोई त्वरित न्यूनतम समाधान की तलाश में है,

import signal

# The code which crashes program on interruption

signal.signal(signal.SIGINT, call_this_function_if_interrupted)

# The code skipped if interrupted
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.