2010-05-26 11 views
12

में अपवादों को सही तरीके से कैसे संभालना चाहिए, मुझे समझ में नहीं आ रहा है कि मुझे 'यहां और अब' किस तरह के अपवादों को संभालना चाहिए, और मुझे किस तरह के अपवादों को फिर से उठाना चाहिए या बस यहां संभाल नहीं करना चाहिए, और क्या करना है बाद में उनके साथ (उच्च स्तर पर) करें। उदाहरण के लिए: मैंने एसएसएल संचार के साथ python3 का उपयोग कर क्लाइंट/सर्वर एप्लिकेशन लिखा था। क्लाइंट को उन पर किसी भी अंतर पर फ़ाइलों को सत्यापित करना है, और यदि diff मौजूद है तो इसे सर्वर पर 'अपडेट' फ़ाइल भेजनी चाहिए।मुझे Python3


class BasicConnection: 
    #blablabla 
    def sendMessage(self, sock, url, port, fileToSend, buffSize): 
     try: 
      sock.connect((url, port)) 
      while True: 
       data = fileToSend.read(buffSize) 
       if not data: break 
       sock.send(data) 
      return True 
     except socket.timeout as toErr: 
      raise ConnectionError("TimeOutError trying to send File to remote socket: %s:%d" 
            % (url,port)) from toErr 
     except socket.error as sErr: 
      raise ConnectionError("Error trying to send File to remote socket: %s:%d" 
            % (url,port)) from sErr 
     except ssl.SSLError as sslErr: 
      raise ConnectionError("SSLError trying to send File to remote socket: %s:%d" 
            % (url,port)) from sslErr 
     finally: 
      sock.close() 

क्या यह पाइथन में अपवादों का उपयोग करने का सही तरीका है? समस्या यह है: क्या होगा अगर file.read() IOError फेंकता है? क्या मुझे इसे यहां संभालना चाहिए, या बस कुछ भी नहीं करना चाहिए और बाद में इसे पकड़ना चाहिए? और कई अन्य संभावित अपवाद?

  1. क्लाइंट सर्वर से अपडेट की गई फ़ाइलें भेजने के लिए इस वर्ग (BasicConnection) का उपयोग करें:

class PClient(): 
    def __init__(self, DATA): 
     '''DATA = { 'sendTo'  : {'host':'','port':''}, 
        'use_ssl'  : {'use_ssl':'', 'fileKey':'', 'fileCert':'', 'fileCaCert':''}, 
        'dirToCheck' : '', 
        'localStorage': '', 
        'timeToCheck' : '', 
        'buffSize' : '', 
        'logFile'  : ''} ''' 
     self._DATA = DATA 
     self._running = False 
     self.configureLogging() 


    def configureLogging(self): 
     #blablabla 

    def isRun(self): 
     return self._running 

    def initPClient(self): 
     try: 
      #blablabla 

      return True 
     except ConnectionError as conErr: 
      self._mainLogger.exception(conErr) 
      return False 
     except FileCheckingError as fcErr: 
      self._mainLogger.exception(fcErr) 
      return False 
     except IOError as ioErr: 
      self._mainLogger.exception(ioErr) 
      return False 
     except OSError as osErr: 
      self._mainLogger.exception(osErr) 
      return False 


    def startPClient(self): 
     try: 
      self._running = True 
      while self.isRun(): 
       try : 
        self._mainLogger.debug("Checking differences") 
        diffFiles = FileChecker().checkDictionary(self._dict) 

        if len(diffFiles) != 0: 
         for fileName in diffFiles: 
          try: 
           self._mainLogger.info("Sending updated file: %s to remote socket: %s:%d" 
            % (fileName,self._DATA['sendTo']['host'],self._DATA['sendTo']['port'])) 
           fileToSend = io.open(fileName, "rb") 
           result = False 
           result = BasicConnection().sendMessage(self._sock, self._DATA['sendTo']['host'], 
                     self._DATA['sendTo']['port'], fileToSend, self._DATA['buffSize']) 
           if result: 
            self._mainLogger.info("Updated file: %s was successfully delivered to remote socket: %s:%d" 
            % (fileName,self._DATA['sendTo']['host'],self._DATA['sendTo']['port'])) 
          except ConnectionError as conErr: 
           self._mainLogger.exception(conErr) 
          except IOError as ioErr: 
           self._mainLogger.exception(ioErr) 
          except OSError as osErr: 
           self._mainLogger.exception(osErr) 

         self._mainLogger.debug("Updating localStorage %s from %s " %(self._DATA['localStorage'], self._DATA['dirToCheck'])) 
         FileChecker().updateLocalStorage(self._DATA['dirToCheck'], 
                 self._DATA['localStorage']) 
        self._mainLogger.info("Directory %s were checked" %(self._DATA['dirToCheck'])) 
        time.sleep(self._DATA['timeToCheck']) 
       except FileCheckingError as fcErr: 
        self._mainLogger.exception(fcErr) 
       except IOError as ioErr: 
        self._mainLogger.exception(ioErr) 
       except OSError as osErr: 
        self._mainLogger.exception(osErr) 
     except KeyboardInterrupt: 
      self._mainLogger.info("Shutting down...") 
      self.stopPClient() 
     except Exception as exc: 
      self._mainLogger.exception(exc) 
      self.stopPClient() 
      raise RuntimeError("Something goes wrong...") from exc 

    def stopPClient(self): 
     self._running = False 

यह सही है? क्या कोई अपना समय बिता सकता है और अपवादों को संभालने की पाइथोनिक शैली को समझने में मेरी मदद कर सकता है? मैं समझ नहीं पा रहा हूं कि नाम अपर, टाइप एरर, कीरर, वैल्यूएरर ... और इतने पर ऐसे अपवादों के साथ क्या करना है ....... उन्हें किसी भी वक्त किसी भी वक्तव्य में फेंक दिया जा सकता है ... और क्या करना है उनके साथ, अगर मैं सबकुछ लॉग करना चाहता हूं।

  1. और लोगों को आम तौर पर किस जानकारी को लॉग करना चाहिए? अगर त्रुटि होती है, तो इसके बारे में मुझे क्या जानकारी चाहिए? सभी ट्रेसबैक, या इसके बारे में सिर्फ प्रासंगिक संदेश या कुछ और?

  2. मुझे उम्मीद है कि कोई मेरी मदद करेगा। बहुत बहुत धन्यवाद।

उत्तर

24

सामान्य तौर पर, आपको चाहिए "पकड़" अपवाद है कि आप ऐसा करने के लिए (क्योंकि वे उपयोगकर्ता त्रुटि, या अपने कार्यक्रम के नियंत्रण से बाहर अन्य पर्यावरणीय समस्याओं की वजह से हो सकता है), विशेष रूप से यदि आप जानते हैं उम्मीद है कि आपके कोड उनके बारे में करने में सक्षम हो सकता है। एक त्रुटि रिपोर्ट में बस अधिक विवरण देना एक मामूली मुद्दा है, हालांकि कुछ प्रोग्रामों के चश्मे को ऐसा करने की आवश्यकता हो सकती है (उदाहरण के लिए एक लंबे समय से चलने वाला सर्वर जो ऐसी समस्याओं के कारण क्रैश नहीं होना चाहिए, बल्कि बहुत सारी राज्य जानकारी लॉग करें, उपयोगकर्ता एक सारांश स्पष्टीकरण, और भविष्य के प्रश्नों के लिए काम करते रहें)।

NameError, TypeError, KeyError, ValueError, SyntaxError, AttributeError, और इतने पर, इस कार्यक्रम में त्रुटियों के रूप में होने के कारण के बारे में सोचा जा सकता है - कीड़े, नहीं प्रोग्रामर के नियंत्रण से बाहर की समस्याओं। यदि आप लाइब्रेरी या फ्रेमवर्क जारी कर रहे हैं, तो आपके कोड को आपके नियंत्रण के बाहर दूसरे कोड द्वारा बुलाया जा रहा है, तो ऐसी बग्स अन्य कोड में हो सकती हैं; आपको आम तौर पर अन्य प्रोग्रामर की अपनी बग डीबग करने में मदद करने के लिए अपवाद प्रसार करना चाहिए। यदि आप एक एप्लिकेशन जारी कर रहे हैं, तो आप बग के मालिक हैं, और आपको वह रणनीति चुननी होगी जो आपको ढूंढने में मदद करे।

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

अधिकांश बग कोर्स के अपने परीक्षण के दौरान दिखाना चाहिए; उस स्थिति में, अपवाद का प्रचार करना उपयोगी है क्योंकि आप इसे डीबगर पर लगा सकते हैं और बग के विवरणों का पता लगा सकते हैं।

कभी-कभी इन जैसे कुछ अपवाद केवल इसलिए दिखते हैं क्योंकि "अनुमति से क्षमा मांगना आसान है" (ईएएफपी) - पायथन में पूरी तरह से स्वीकार्य प्रोग्रामिंग तकनीक। उस स्थिति में आपको उन्हें एक साथ संभालना चाहिए। उदाहरण के लिए:

try: 
    return mylist[theindex] 
except IndexError: 
    return None 
यहाँ

आप उम्मीद कर सकते है कि आम तौर पर theindexmylist में एक वैध सूचकांक है, लेकिन कभी-कभी mylist की सीमा से बाहर है - और बाद के मामले, प्राक्कल्पित एप्लिकेशन के शब्दों से जो इस स्निपेट में संबंधित है, कोई त्रुटि नहीं है, सूची को None एस की अनंत संख्या के साथ दोनों पक्षों पर अवधारणात्मक रूप से विस्तारित करने पर विचार करके तय किया जाना चाहिए। सूचकांक के सकारात्मक और नकारात्मक मूल्यों की उचित जांच करने के बजाय बस कोशिश करना/छोड़ना आसान है (और सीमाएं, यदि सीमा से बाहर होना वास्तव में दुर्लभ घटना है)।

KeyError और AttributeError के लिए इसी तरह उपयुक्त मामलों कम बार होता है, आदि dicts की getattr निर्मित और get विधि करने के लिए धन्यवाद (जो आप एक डिफ़ॉल्ट मान प्रदान करते हैं), collections.defaultdict,; लेकिन सूचियों का उनमें कोई प्रत्यक्ष समकक्ष नहीं है, इसलिए कोशिश/छोड़कर IndexError के लिए अधिक बार देखा जाता है।

वाक्यविन्यास त्रुटियों को पकड़ने की कोशिश कर रहे हैं, त्रुटियों, मान त्रुटियों, नाम त्रुटियों आदि टाइप करें, थोड़ा दुर्लभ और अधिक विवादास्पद है - हालांकि यह निश्चित रूप से उपयुक्त होगा अगर त्रुटि "प्लग-इन" में निदान की गई हो, तीसरा - आपके नियंत्रण के बाहर -पार्टी कोड जो आपका ढांचा/अनुप्रयोग गतिशील रूप से लोड करने और निष्पादित करने की कोशिश कर रहा है (वास्तव में वह मामला है जहां आप लाइब्रेरी की आपूर्ति कर रहे हैं या जैसे ही आपके नियंत्रण से बाहर कोड के साथ शांतिपूर्वक सह-अस्तित्व में रहने की आवश्यकता है जो अच्छी तरह से छोटी हो सकती है) । टाइप और मान त्रुटियां कभी-कभी ईएएफपी पैटर्न के भीतर होती हैं - उदा। जब आप स्ट्रिंग या संख्या को स्वीकार करने के लिए किसी फ़ंक्शन को अधिभारित करने का प्रयास करते हैं और प्रत्येक मामले में थोड़ा अलग व्यवहार करते हैं, तो ऐसी त्रुटियों को पकड़ना प्रकारों की जांच करने से बेहतर हो सकता है - लेकिन इस प्रकार अधिभारित कार्यों की अवधारणा काफी अधिक नहीं होती है संदिग्ध।

"उपयोगकर्ता और पर्यावरणीय त्रुटियों" पर वापस, उपयोगकर्ता अनिवार्य रूप से गलतियां करते हैं जब वे आपको इनपुट देते हैं, एक फ़ाइल नाम इंगित करें जो वास्तव में आसपास नहीं है (या आपके पास पढ़ने की अनुमति नहीं है, या लिखने के लिए यदि आप यही हैं 'ऐसा करने वाला होना चाहिए), और इसी तरह: इस तरह की सभी त्रुटियों को निश्चित रूप से पकड़ा जाना चाहिए और परिणामस्वरूप उपयोगकर्ता के बारे में स्पष्ट स्पष्टीकरण में क्या गलत हो गया है, और इनपुट का अधिकार प्राप्त करने का एक और मौका है। नेटवर्क कभी-कभी नीचे जाते हैं, डेटाबेस या अन्य बाहरी सर्वर अपेक्षित प्रतिक्रिया नहीं दे सकते हैं, और आगे - कभी-कभी ऐसी समस्याओं को पकड़ने और पुनः प्रयास करने के लायक होते हैं (शायद थोड़ी देर के बाद - शायद उपयोगकर्ता के संकेत के साथ क्या गलत है, उदाहरण के लिए वे हो सकता है कि गलती से केबल को अनप्लग किया हो और आप उन्हें चीजों को ठीक करने का मौका देना चाहते हैं और आपको फिर से प्रयास करने के बारे में बताते हैं), कभी-कभी (विशेष रूप से अप्रत्याशित लंबे समय से चलने वाले कार्यक्रमों में) कुछ भी नहीं है जो आप आदेशित शट डाउन को छोड़कर कर सकते हैं (और विस्तृत लॉगिंग पर्यावरण के हर संभव-प्रासंगिक पहलू का)।

तो, संक्षेप में, आपके क्यू के शीर्षक का उत्तर है, "यह निर्भर करता है" ;-)। मुझे आशा है कि मैं ऐसी कई स्थितियों और पहलुओं को सूचीबद्ध करने में उपयोग कर रहा हूं जिन पर यह निर्भर हो सकता है, और इस तरह के मुद्दों की ओर ले जाने के लिए आम तौर पर सबसे उपयोगी दृष्टिकोण की सिफारिश करना।

+1

कुछ "पेट भावनाओं" उस के साथ आने का उपयोगी आत्मनिरीक्षण अजगर के साथ अनुभव। मैं उत्सुकता से आपके पायथन सर्वोत्तम अभ्यासों की प्रतीक्षा करता हूं - लेकिन पाइथन पैटर्न से पहले नहीं :) –

3

आरंभ करने के लिए, आपको किसी भी _mainLogger की आवश्यकता नहीं है। यदि आप किसी भी अपवाद को पकड़ना चाहते हैं, तो शायद उन्हें ईमेल या जो कुछ भी लॉग या भेजना है, वह उच्चतम संभव स्तर पर करें - निश्चित रूप से इस कक्षा के अंदर नहीं।

इसके अलावा, आप निश्चित रूप से प्रत्येक अपवाद को एक रनटाइमर में परिवर्तित नहीं करना चाहते हैं। इसे उभारा दो। StopClient() विधि का अभी कोई उद्देश्य नहीं है। जब यह होगा, हम इसे देखेंगे ..

आप मूल रूप से ConnectionError, IOError और OSError एक साथ लपेट सकता है (जैसे, कुछ और ही रूप में फिर से बढ़ा है), लेकिन नहीं है कि तुलना में भी बहुत कुछ ...

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