2015-11-04 11 views
9

मैं सॉल्टस्टैक के लिए कुछ आदि मॉड्यूल लिख रहा था और इस अजीब मुद्दे में भाग गया जहां यह मुझे किसी अपवाद को पकड़ने से रोक रहा है और मुझे इसमें दिलचस्पी है कि यह कैसा चल रहा है। ऐसा लगता है कि विशेष रूप से urllib3 के आसपास केंद्रित है।मैं इस पायथन अपवाद को क्यों नहीं पकड़ सकता?

एक छोटा सा स्क्रिप्ट (नहीं नमक):

import etcd 
c = etcd.Client('127.0.0.1', 4001) 
print c.read('/test1', wait=True, timeout=2) 

और जब हम इसे चलाने:

[[email protected] utils]# /tmp/etcd_watch.py 
Traceback (most recent call last): 
    File "/tmp/etcd_watch.py", line 5, in <module> 
    print c.read('/test1', wait=True, timeout=2) 
    File "/usr/lib/python2.6/site-packages/etcd/client.py", line 481, in read 
    timeout=timeout) 
    File "/usr/lib/python2.6/site-packages/etcd/client.py", line 788, in api_execute 
    cause=e 
etcd.EtcdConnectionFailed: Connection to etcd failed due to ReadTimeoutError("HTTPConnectionPool(host='127.0.0.1', port=4001): Read timed out.",) 

ठीक है, चलो कि मैथुन पकड़ने करते हैं:

#!/usr/bin/python 

import etcd 
c = etcd.Client('127.0.0.1', 4001) 

try: 
    print c.read('/test1', wait=True, timeout=2) 
except etcd.EtcdConnectionFailed: 
    print 'connect failed' 

भागो यह:

[[email protected] _modules]# /tmp/etcd_watch.py 
connect failed 

अच्छा लगता है - यह सब काम कर रहे अजगर है। तो क्या मुद्दा है? मैं नमक etcd मॉड्यूल में इस है:

[[email protected] _modules]# cat sjmh.py 
import etcd 

def test(): 
    c = etcd.Client('127.0.0.1', 4001) 
    try: 
    return c.read('/test1', wait=True, timeout=2) 
    except etcd.EtcdConnectionFailed: 
    return False 

और जब हम चलने वाले:

[[email protected] _modules]# salt 'alpha' sjmh.test 
alpha: 
    The minion function caused an exception: Traceback (most recent call last): 
     File "/usr/lib/python2.6/site-packages/salt/minion.py", line 1173, in _thread_return 
     return_data = func(*args, **kwargs) 
     File "/var/cache/salt/minion/extmods/modules/sjmh.py", line 5, in test 
     c.read('/test1', wait=True, timeout=2) 
     File "/usr/lib/python2.6/site-packages/etcd/client.py", line 481, in read 
     timeout=timeout) 
     File "/usr/lib/python2.6/site-packages/etcd/client.py", line 769, in api_execute 
     _ = response.data 
     File "/usr/lib/python2.6/site-packages/urllib3/response.py", line 150, in data 
     return self.read(cache_content=True) 
     File "/usr/lib/python2.6/site-packages/urllib3/response.py", line 218, in read 
     raise ReadTimeoutError(self._pool, None, 'Read timed out.') 
    ReadTimeoutError: HTTPConnectionPool(host='127.0.0.1', port=4001): Read timed out. 

मानव संसाधन विकास मंत्री कि अजीब है। आदि के पढ़ने को वापस भेजना चाहिए था। etcdConnectionFailed। तो, चलो इसे आगे देखो। हमारे मॉड्यूल अब यह है:

import etcd 

def test(): 
    c = etcd.Client('127.0.0.1', 4001) 
    try: 
    return c.read('/test1', wait=True, timeout=2) 
    except Exception as e: 
    return str(type(e)) 

और हम पाते हैं:

[[email protected] _modules]# salt 'alpha' sjmh.test 
alpha: 
    <class 'urllib3.exceptions.ReadTimeoutError'> 

ठीक है, इसलिए हम जानते हैं कि हम इस बात पकड़ कर सकते हैं। और अब हम जानते हैं कि इसे रीडटाइम एरर फेंक दिया गया है, तो चलिए इसे पकड़ लें। हमारे मॉड्यूल के नवीनतम संस्करण:

import etcd 
import urllib3.exceptions 

def test(): 
    c = etcd.Client('127.0.0.1', 4001) 
    try: 
    c.read('/test1', wait=True, timeout=2) 
    except urllib3.exceptions.ReadTimeoutError as e: 
    return 'caught ya!' 
    except Exception as e: 
    return str(type(e)) 

और हमारे परीक्षण ..

[[email protected] _modules]# salt 'alpha' sjmh.test 
alpha: 
    <class 'urllib3.exceptions.ReadTimeoutError'> 

एर, रुको, क्या? हमने उसे क्यों नहीं पकड़ा? अपवाद काम करते हैं, है ना?

कैसे के बारे में अगर हम कोशिश करते हैं और urllib3 से आधार वर्ग को पकड़ने ..

[[email protected] _modules]# cat sjmh.py 
import etcd 
import urllib3.exceptions 

def test(): 
    c = etcd.Client('127.0.0.1', 4001) 
    try: 
    c.read('/test1', wait=True, timeout=2) 
    except urllib3.exceptions.HTTPError: 
    return 'got you this time!' 

आशा और प्रार्थना करता हूँ ..

[[email protected] _modules]# salt 'alpha' sjmh.test 
alpha: 
    The minion function caused an exception: Traceback (most recent call last): 
     File "/usr/lib/python2.6/site-packages/salt/minion.py", line 1173, in _thread_return 
     return_data = func(*args, **kwargs) 
     File "/var/cache/salt/minion/extmods/modules/sjmh.py", line 7, in test 
     c.read('/test1', wait=True, timeout=2) 
     File "/usr/lib/python2.6/site-packages/etcd/client.py", line 481, in read 
     timeout=timeout) 
     File "/usr/lib/python2.6/site-packages/etcd/client.py", line 769, in api_execute 
     _ = response.data 
     File "/usr/lib/python2.6/site-packages/urllib3/response.py", line 150, in data 
     return self.read(cache_content=True) 
     File "/usr/lib/python2.6/site-packages/urllib3/response.py", line 218, in read 
     raise ReadTimeoutError(self._pool, None, 'Read timed out.') 
    ReadTimeoutError: HTTPConnectionPool(host='127.0.0.1', port=4001): Read timed out. 

ब्लास्ट YE! ठीक है, आइए एक अलग विधि का प्रयास करें जो एक अलग आदि अपवाद देता है। हमारे मॉड्यूल अब इस तरह दिखता है:

import etcd 

def test(): 
    c = etcd.Client('127.0.0.1', 4001) 
    try: 
    c.delete('/') 
    except etcd.EtcdRootReadOnly: 
    return 'got you this time!' 

और हमारे रन:

[[email protected] _modules]# salt 'alpha' sjmh.test 
alpha: 
    got you this time! 

एक अंतिम परीक्षण के रूप में, मैं इस मॉड्यूल है, जो मैं चला सकते हैं या तो सीधे अजगर से, या एक नमक मॉड्यूल के रूप में बनाया है। ।

import etcd 
import urllib3 

def test(): 
    c = etcd.Client('127.0.0.1', 4001) 
    try: 
    c.read('/test1', wait=True, timeout=2) 
    except urllib3.exceptions.ReadTimeoutError: 
    return 'got you this time!' 
    except etcd.EtcdConnectionFailed: 
    return 'cant get away from me!' 
    except etcd.EtcdException: 
    return 'oh no you dont' 
    except urllib3.exceptions.HTTPError: 
    return 'get back here!' 
    except Exception as e: 
    return 'HOW DID YOU GET HERE? {0}'.format(type(e)) 

if __name__ == "__main__": 
    print test() 

अजगर के माध्यम से:

[[email protected] _modules]# python ./sjmh.py 
cant get away from me! 

नमक के माध्यम से:

[[email protected] _modules]# salt 'alpha' sjmh.test 
alpha: 
    HOW DID YOU GET HERE? <class 'urllib3.exceptions.ReadTimeoutError'> 

तो, हम etcd से अपवाद पकड़ कर सकते हैं कि यह फेंकता है। लेकिन, जब हम आम तौर पर urllib3 को पकड़ने में सक्षम होते हैं, जब हम अपने अकेले द्वारा पायथन-आदि चलाते हैं, जब मैं इसे नमक के माध्यम से चलाता हूं, तो कुछ भी नहीं होता है, जो कि कंबल 'अपवाद' खंड को छोड़कर उस urllib3 अपवाद को पकड़ने में सक्षम नहीं होता है।

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

संपादित करें:

तो मैं अंत में इसे पकड़ने में सक्षम था।

import etcd 
import urllib3.exceptions 
from urllib3.exceptions import ReadTimeoutError 

def test(): 
    c = etcd.Client('127.0.0.1', 4001) 
    try: 
    c.read('/test1', wait=True, timeout=2) 
    except urllib3.exceptions.ReadTimeoutError: 
    return 'caught 1' 
    except urllib3.exceptions.HTTPError: 
    return 'caught 2' 
    except ReadTimeoutError: 
    return 'caught 3' 
    except etcd.EtcdConnectionFailed as ex: 
    return 'cant get away from me!' 
    except Exception as ex: 
    return 'HOW DID YOU GET HERE? {0}'.format(type(ex)) 

if __name__ == "__main__": 
    print test() 

और जब रन:

[[email protected] _modules]# salt 'alpha' sjmh.test 
alpha: 
    caught 3 

हालांकि यह अभी भी मतलब नहीं है। अपवादों के बारे में मुझे क्या पता है, वापसी 'पकड़ा 1' होना चाहिए। पूर्ण वर्ग नाम का उपयोग करने के बजाय मुझे अपवाद का नाम सीधे आयात क्यों करना चाहिए?

अधिक संपादन!

तो, दो वर्गों के बीच तुलना जोड़ने से 'झूठी' उत्पन्न होती है - जो कि स्पष्ट है, क्योंकि छोड़कर खंड काम नहीं कर रहा था, इसलिए वे समान नहीं हो सकते थे।

मैंने c.read() को कॉल करने से ठीक पहले, स्क्रिप्ट में निम्नलिखित जोड़ा।

log.debug(urllib3.exceptions.ReadTimeoutError.__module__) 
log.debug(ReadTimeoutError.__module__) 

और अब मैं लॉग में इस मिल:

[DEBUG ] requests.packages.urllib3.exceptions 
[DEBUG ] urllib3.exceptions 

तो, कारण है कि जिस तरह से यह है पकड़े जाने प्रतीत होता है कि। यह सिर्फ etcd डाउनलोड करके भी प्रतिलिपि प्रस्तुत करने योग्य है और इस तरह पुस्तकालय और कुछ कर रही अनुरोध करता है:

#!/usr/bin/python 

#import requests 
import etcd 

c = etcd.Client('127.0.0.1', 4001) 
c.read("/blah", wait=True, timeout=2) 

आप 'सही' अपवाद उठाया हो रही अंत होगा - etcd.EtcdConnectionFailed। हालांकि, 'अनुरोध' को अपूर्ण करें और आप urllib3.exceptions.ReadTimeoutError के साथ समाप्त हो जाएंगे, क्योंकि आदि अब अपवाद को पकड़ नहीं लेता है।

तो ऐसा प्रतीत होता है कि जब अनुरोध आयात किया जाता है, तो यह urllib3 अपवादों को फिर से लिखता है, और किसी अन्य मॉड्यूल को पकड़ने की कोशिश कर रहा है, विफल रहता है। साथ ही, ऐसा लगता है कि अनुरोधों के नए संस्करणों में यह समस्या नहीं है।

+0

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

+0

पेंचिया, मैंने कहा कि इसे वापस लौटा देना चाहिए क्योंकि आम तौर पर, पाइथन-आदि 0.4.2 के साथ, जब टाइमआउट प्रतीक्षा प्रतीक्षा पर समाप्त हो जाता है, तो यह इत्यादि बढ़ाता है। एटीसीडीकनेक्शनफेल। यह एक स्थानीय आदि उदाहरण है और मैंने सत्यापित किया कि आदि था। साथ ही, जैसा कि अंतिम लिपि द्वारा प्रमाणित है, नेटवर्किंग के अलावा कुछ और चल रहा है। – sjmh

+0

ओह हाँ। यह एक और कारण है कि कनेक्शन विफल हो सकता है। लेकिन आपके मामले में रीड टाइमआउट के कारण विफल हो रहा है। उस ने कहा, पुस्तकालय अजीब व्यवहार कर सकता है। – Pynchia

उत्तर

3

मेरा उत्तर नीचे अटकलें है, क्योंकि मैं इसे सटीक पुस्तकालयों के साथ अभ्यास पर साबित नहीं कर सकता (शुरू करने के लिए मैं आपकी त्रुटि को पुन: उत्पन्न नहीं कर सकता क्योंकि यह पुस्तकालय संस्करणों पर निर्भर करता है और वे कैसे स्थापित होते हैं), लेकिन फिर भी दिखाता है इस होने के संभावित तरीकों में से एक:

अंतिम उदाहरण एक अच्छा सुराग देता है: बिंदु वास्तव में प्रोग्राम निष्पादन के समय विभिन्न क्षणों पर है, नाम urllib3.exceptions.ReadTimeoutError विभिन्न वर्गों का उल्लेख कर सकता है। ReadTimeoutError, पायथन में हर दूसरे मॉड्यूल की तरह है, urllib3.exceptions नेमस्पेस, में बस एक नाम है और को फिर से सौंप दिया जा सकता है (लेकिन इसका मतलब यह नहीं है कि ऐसा करना एक अच्छा विचार है)।

इस नाम का पूर्णत: योग्य "पथ" द्वारा संदर्भित करते समय - हम इसकी वास्तविक स्थिति को संदर्भित करते समय उस समय तक संदर्भित करने की गारंटी देते हैं। हालांकि, जब हम इसे पहले from urllib3.exceptions import ReadTimeoutError जैसे आयात करते हैं - यह नाम ReadTimeoutError नामस्थान में नाम लाता है जो आयात करता है, और यह नाम urllib3.exceptions.ReadTimeoutError के मान से इस आयात के समय तक सीमित है। अब, अगर कुछ अन्य कोड बाद में urllib3.exceptions.ReadTimeoutError के मान को पुन: असाइन करते हैं - दो (इसका "वर्तमान"/"नवीनतम" मान और पहले आयातित एक) वास्तव में अलग हो सकता है - इसलिए तकनीकी रूप से आप दो अलग-अलग वर्गों को समाप्त कर सकते हैं। अब, कौन सी अपवाद कक्षा वास्तव में उठाई जाएगी - यह इस बात पर निर्भर करता है कि त्रुटि किस प्रकार उत्पन्न करती है इसका उपयोग करता है: यदि उन्होंने पहले ReadTimeoutError को उनके नामस्थान में आयात किया था - तो यह एक (मूल ") उठाया जाएगा। कहीं ऐसा तो नहीं कि आप except ReadTimeoutError ब्लॉक के लिए निम्न जोड़ सकते है

सत्यापित करने के लिए: यह प्रिंट तो False

print(urllib3.exceptions.ReadTimeoutError == ReadTimeoutError) 

- यह साबित करता है कि समय अपवाद उठाया है द्वारा, दो "संदर्भ" का संदर्भ लें वास्तव में विभिन्न वर्गों के लिए।


एक गरीब कार्यान्वयन जो समान परिणाम प्राप्त हो सकते हैं का एक सरल उदाहरण:

फ़ाइल api.py (ठीक से डिजाइन और अपने आप खुशी से मौजूद है):

class MyApiException(Exception): 
    pass 

def foo(): 
    raise MyApiException('BOOM!') 

फ़ाइल apibreaker.py (इसके लिए जिम्मेदार एक):

import api 

class MyVeryOwnException(Exception): 
    # note, this doesn't extend MyApiException, 
    # but creates a new "branch" in the hierarhcy 
    pass 

# DON'T DO THIS AT HOME! 
api.MyApiException = MyVeryOwnException 

फ़ाइल apiuser.py:

import api 
from api import MyApiException, foo 
import apibreaker 

if __name__ == '__main__': 
    try: 
     foo() 
    except MyApiException: 
     print("Caught exception of an original class") 
    except api.MyApiException: 
     print("Caught exception of a reassigned class") 

मार डाला जब:

$ python apiuser.py 
Caught exception of a reassigned class 

आप लाइन import apibreaker निकालते हैं - स्पष्ट रूप से तो सभी के रूप में यह होना चाहिए अपने स्थानों को वापस चला जाता।

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

+0

मैंने वास्तव में कई दिनों पहले एक बहुत ही समान परीक्षण का सुझाव दिया था, लेकिन इसे बिना किसी स्पष्टीकरण के नीचे वोट दिया गया था, इसलिए मैंने अपना जवाब हटा दिया। मेरा सुझाव वास्तव में प्राप्त अपवाद के साथ पकड़े जाने वाले अपवाद की तुलना करना था ताकि यह देखने के बावजूद कि वे * वास्तव में * समान हैं या नहीं। मुझे नहीं पता कि इसे किसने वोट दिया। –

+0

इसके लिए धन्यवाद - यह मुझे उस मार्ग से नीचे ले जाता है जो मुझे लगता है कि उत्तर प्रदान करता है और जब इसे सॉल्टस्टैक के माध्यम से चलाया जाता है, तो नमक-स्टैक अनुरोधों में आयात कर रहा है, जो urllib3 अपवादों को फिर से सौंप रहा है। Urllib3.exceptions.ReadTimeoutError अब request.packages.urllib3.exceptions से संबंधित है, जहां ReadTimeoutError को पुनः असाइन नहीं किया गया है और अभी भी urllib3.exceptions मॉड्यूल से है। एटीसीडी urllib3 संस्करण को पकड़ने की कोशिश कर रहा है, अनुरोधों को एक नहीं और इसलिए यह गुजरता है। इसके अलावा, @ टॉमकारज़, यकीन नहीं है कि किसने नीचे गिराया, लेकिन मैं नहीं था! – sjmh

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