2016-05-22 4 views
5

मैंने cmd मॉड्यूल का उपयोग करके एक पायथन 3.5 एप्लिकेशन लिखा था। आखिरी चीज जिसे मैं कार्यान्वित करना चाहता हूं वह सीटीआरएल-सी (सिग्ंट) सिग्नल का उचित संचालन है।पायथन सीएमडी मॉड्यूल में CTRL-C संभाल

  • प्रिंट^सी बिंदु पर कर्सर
  • स्पष्ट बफर है ताकि इनपुट पाठ
  • हटा दी जाती है अगले पर जाएं: मैं इसे कम या ज्यादा व्यवहार करने के लिए जिस तरह से बैश यह करता है चाहते हैं लाइन, शीघ्र प्रिंट, और

मूल रूप से इनपुट के लिए प्रतीक्षा:

/test $ bla bla bla| 
# user types CTRL-C 
/test $ bla bla bla^C 
/test $ 

यहाँ एक runnable के रूप में कोड सरल है नमूना:

import cmd 
import signal 


class TestShell(cmd.Cmd): 
    def __init__(self): 
     super().__init__() 

     self.prompt = '$ ' 

     signal.signal(signal.SIGINT, handler=self._ctrl_c_handler) 
     self._interrupted = False 

    def _ctrl_c_handler(self, signal, frame): 
     print('^C') 
     self._interrupted = True 

    def precmd(self, line): 
     if self._interrupted: 
      self._interrupted = False 
      return '' 

     if line == 'EOF': 
      return 'exit' 

     return line 

    def emptyline(self): 
     pass 

    def do_exit(self, line): 
     return True 


TestShell().cmdloop() 

यह लगभग काम करता है। जब मैं CTRL-C दबाता हूं,^सी कर्सर पर मुद्रित होता है, लेकिन मुझे अभी भी एंटर दबा देना है। फिर, precmd विधि हैंडलर द्वारा सेट किए गए self._interrupted ध्वज को नोटिस करती है, और एक खाली रेखा लौटाती है। यह वह जगह है जहां तक ​​मैं इसे ले सकता हूं, लेकिन मैं किसी भी तरह से उस प्रविष्टि को दबा नहीं देना चाहता हूं।

मुझे लगता है कि मुझे किसी भी तरह से input() को वापस करने की आवश्यकता है, क्या किसी के पास विचार हैं?

+0

यह एक छोटा सा चलने योग्य नमूना रखने में मदद करेगा। अपने कोड को चलाने के बिना आप वर्तमान में इसके बारे में कैसे जा रहे हैं, ''\ ​​n'' या खाली' stdout' 'पर रिक्त लिखेंगे? सिग्नल हैंडलर को एक बार '' सी ' – tijko

+1

@tijko के अंत में एक नई लाइन का सामना करना पड़ता है, स्निपेट को एक चलने योग्य नमूने के साथ बदलकर पोस्ट संपादित किया जाता है। मैंने stdout के लिए एक नई लाइन लिखने की कोशिश की (यह वास्तव में प्रिंट विधि द्वारा डिफ़ॉल्ट रूप से किया जाता है), लेकिन यह काम नहीं करता है, और यह क्यों होगा? यह stdout से stdin खिलाया जाना चाहिए, जो मामला नहीं है, यह पाइप नहीं है। – wujek

+0

हाँ, आप सही हैं मैं कमांड लाइन stdin प्रॉम्प्ट के मामले में नहीं सोच रहा था। @ डैनगेटज़ का एक उत्तर है जो आपकी आवश्यकताओं के अनुरूप होगा। इसके क्लीनर भी, सिग्नल हैंडलर सेट या सेट झंडे सेट करने की जरूरत नहीं है। आपकी टिप्पणी के बाद मैंने स्टडीन को रीडायरेक्ट करने की लाइनों के साथ सोचना शुरू कर दिया, लेकिन आपको आवश्यकता होने पर समायोजित कर सकते हैं। – tijko

उत्तर

3

मुझे Ctrl-C के साथ इच्छित व्यवहार को प्राप्त करने के लिए कुछ हैकी तरीके मिले।

सेट use_rawinput=False और cmd.Cmd की सार्वजनिक इंटरफ़ेस करने के लिए stdin

यह एक लाठी (कम या ज्यादा ...) की जगह। दुर्भाग्य से, यह रीडलाइन समर्थन अक्षम करता है।

आप गलत पर use_rawinput सेट और एक अलग फ़ाइल जैसी वस्तु Cmd.__init__() में stdin को बदलने के लिए पारित कर सकते हैं। अभ्यास में, इस ऑब्जेक्ट पर केवल readline() कहा जाता है। तो आपको लगता है कि KeyboardInterrupt लग जाती है और व्यवहार निष्पादित करता stdin के लिए एक आवरण बना सकते हैं आप के बजाय हैं:

class _Wrapper: 

    def __init__(self, fd): 
     self.fd = fd 

    def readline(self, *args): 
     try: 
      return self.fd.readline(*args) 
     except KeyboardInterrupt: 
      print() 
      return '\n' 


class TestShell(cmd.Cmd): 

    def __init__(self): 
     super().__init__(stdin=_Wrapper(sys.stdin)) 
     self.use_rawinput = False 
     self.prompt = '$ ' 

    def precmd(self, line): 
     if line == 'EOF': 
      return 'exit' 
     return line 

    def emptyline(self): 
     pass 

    def do_exit(self, line): 
     return True 


TestShell().cmdloop() 

जब मैं अपने टर्मिनल पर इस चलाने के लिए, Ctrl-C ^C पता चलता है और एक नई लाइन के लिए स्विच।

बंदर-पैच input()

आप input() का परिणाम चाहते हैं, को छोड़कर आप Ctrl-C के लिए अलग व्यवहार चाहते हैं, एक तरह से करने के लिए कि input() के बजाय एक अलग समारोह का उपयोग करने के होगा:

def my_input(*args): # input() takes either no args or one non-keyword arg 
    try: 
     return input(*args) 
    except KeyboardInterrupt: 
     print('^C') # on my system, input() doesn't show the ^C 
     return '\n' 

हालांकि, अगर आप अंधेरे से input = my_input सेट करते हैं, तो आपको अनंत रिकर्सन मिलता है क्योंकि my_input()input() पर कॉल करेगा, जो अब स्वयं है।लेकिन उस सुधारी जा सकने वाली है, और आप Cmd.cmdloop() दौरान cmd मॉड्यूल अपने संशोधित input() विधि का उपयोग करने में __builtins__ शब्दकोश पैच कर सकते हैं:

def input_swallowing_interrupt(_input): 
    def _input_swallowing_interrupt(*args): 
     try: 
      return _input(*args) 
     except KeyboardInterrupt: 
      print('^C') 
      return '\n' 
    return _input_swallowing_interrupt 


class TestShell(cmd.Cmd): 

    def cmdloop(self, *args, **kwargs): 
     old_input_fn = cmd.__builtins__['input'] 
     cmd.__builtins__['input'] = input_swallowing_interrupt(old_input_fn) 
     try: 
      super().cmdloop(*args, **kwargs) 
     finally: 
      cmd.__builtins__['input'] = old_input_fn 

    # ... 

ध्यान दें कि यह बदलता है सभी Cmd वस्तुओं ही नहीं, TestShell वस्तुओं के लिए input()। यदि यह आप के लिए स्वीकार्य नहीं है, तो आप कर सकते थे ...

कॉपी Cmd.cmdloop() स्रोत है और यह

संशोधित आप इसे उपवर्गीकरण कर रहे हैं, आप cmdloop() कुछ भी आप चाहते हैं कर सकते हैं। "आप जो कुछ भी चाहते हैं" में Cmd.cmdloop() के हिस्सों की प्रतिलिपि बनाना और दूसरों को फिर से लिखना शामिल हो सकता है। या तो किसी अन्य फ़ंक्शन पर कॉल के साथ कॉल को input() पर प्रतिस्थापित करें, या को अपने पुनर्लेखित cmdloop() पर पकड़ें और संभाल लें।

यदि आप पाइथन के नए संस्करणों के साथ अंतर्निहित कार्यान्वयन से डरते हैं, तो आप पूरे cmd मॉड्यूल को एक नए मॉड्यूल में कॉपी कर सकते हैं, और जो भी आप चाहते हैं उसे बदल सकते हैं।

+0

आपके समाधान के लिए धन्यवाद। दुर्भाग्यवश, मैं रीडलाइन का उपयोग कर रहा हूं और यह बहुत महत्वपूर्ण है। – wujek

+0

@ wujek हाँ, मुझे उतना ही उम्मीद थी। खैर, मुझे लगता है कि इसे संभालने के लिए थोड़ा हैकर तरीका है, मैं इसे जोड़ दूंगा। –

+0

मैंने विकल्प 3 लिया, बस cmdloop() कोड कॉपी किया और बदल दिया। मुझे लगता है कि यह कम से कम हैकी समाधान है ... धन्यवाद! – wujek

संबंधित मुद्दे