| title |
|---|
Python / Exception Handling |
Python / Exception Handling
- 8. Errors and Exceptions — Python 3.6.5 documentation #ril
- 8. Errors and Exceptions — Python 2.7.15 documentation #ril
- HandlingExceptions - Python Wiki #ril
- Write Cleaner Python: Use Exceptions (2013-02-06) #ril
- Programming Recommendations - PEP 8 -- Style Guide for Python Code | Python.org 提到一堆 exception 相關注意事項 #ril
- Built-in Exceptions — Python 3.7.1 documentation #ril
- Except where mentioned, they have an “associated value” indicating the detailed cause of the error. This may be a string or a tuple of several items of information (e.g., an error code and a string explaining the code). The associated value is usually passed as arguments to the exception class’s constructor.
- Programmers are encouraged to derive new exceptions from the
Exceptionclass or one of its subclasses, and not fromBaseException. BaseException是所有 built-in exception 的 base class,它的__str__()會回傳 "the representation of the argument(s) to the instance"。這裡解釋了args-- The tuple of arguments given to the exception constructor. Some built-in exceptions (likeOSError) expect a certain number of arguments and assign a special meaning to the elements of this tuple, while others are usually called only with A SINGLE STRING GIVING AN ERROR MESSAGE. 最常見的用法就是單一個 error messsage。
- Exception hierarchy - Built-in Exceptions — Python 3.7.1 documentation #ril
- Exception hierarchy - 6. Built-in Exceptions — Python 2.7.15 documentation #ril
Python 內建有許多 exceptions,很多時候其實是不需要自訂 exceptions 的。常用的有:
-
NotImplementedError尚未完成實作,或是需要 subclass 覆寫並提供實作時。
-
TypeError傳入的參數型態有問題時。
-
ValueError傳入的參數型態沒問題,但值不符合要求時。
-
IndexError內部用 sequence 序列的形式儲存資料,但索引超出範圍時。
-
KeyError內部用 key-value pairs 對照表的形式儲存資料,但索引的 key 值不存在時。
-
AssertionError斷言的條件不成立時。
許多人問到 Java
IllegalStateException適用的狀況,在 Python 裡要丟出什麼例外?目前大家比較能接受的答案是ValueError,也就是第一個參數self的狀態有問題。但我個人認為,如果有人在不對的時機呼叫某個方法,丟出AssertionError應該更為恰當,因為它違反了當初設計 API 的人的假設。 -
RuntimeError當上面的 exceptions 都不適用時。
如果 caller 沒有要針對這種狀況做處理,直接用
RuntimeError並沒有什麼不妥,只要 message 帶有足夠的 debug information 即可。
參考資料:
-
logging.basicConfig(**kwargs)- logging — Logging facility for Python — Python 3.7.3 documentation -stream- ... Note that this argument is incompatible withfilename- if both are present, aValueErroris raised. 看來只要 input 不對,都可以丟ValueError。 -
python - What error to raise when class state is invalid? - Stack Overflow (2012-05-23)
建議延用
ValueError,因為沒有人會去 catch 這類的錯誤(基本上這是 API 用法上的錯誤,屬於 programming error),所以沒必要去自訂 exception。 -
python - Object not in the right state; which exception is appropriate? - Stack Overflow (2011-09-21)
建議自訂 exception,不要跟
ValueError混用,因為使用者會很驚訝except ValueError:時,會 catch 到你的 exception。從 exception handling 的角度來看 exception 要怎麼設計。 -
Is there an analogue to Java IllegalStateException in Python? - Stack Overflow (2009-11-09)
普遍認為
ValueError定位上類似於 Java 的IllegalStateException
-
Programming Recommendations - PEP 8 -- Style Guide for Python Code | Python.org
-
Use EXCEPTION CHAINING appropriately. In Python 3,
raise X from Yshould be used to indicate explicit replacement without losing the original traceback.Python 2 要怎麼做 ??
-
When deliberately REPLACING an inner exception (using "raise X" in Python 2 or "raise X from None" in Python 3.3+), ensure that relevant details are transferred to the new exception (such as preserving the attribute name when converting
KeyErrortoAttributeError, or embedding the text of the original exception in the new exception message).
-
-
Built-in Exceptions — Python 3.7.1 documentation 一開始就提到
raise new_exc from original_exc的用法,跟__cause__有關 #ril -
Wrapping Exceptions - The definitive guide to Python exceptions (2016-08-11) #ril
-
User-defined Exceptions - 8. Errors and Exceptions — Python 3.7.1 documentation When creating a MODULE that can raise several distinct errors, a common practice is to create a BASE CLASS for exceptions defined by that module, and subclass that to create specific exception classes for different error conditions 也就是 application/library 的 exception hierarhcy 會自成一個體系;下面 exception hierarchy 的例子,root 是
Error(Exception),這名稱還滿直覺的 (已在 module 下,不用再寫明 context)。 -
Organization - The definitive guide to Python exceptions (2016-08-11) Library 可以把 exceptions 集中在一個檔案,但 application 則建議按子系統拆開 #ril
-
Python 2's `exceptions` module is missing in Python3, where did its contents go? - Stack Overflow #ril
-
python - Is it better to have many specified exceptions or some general that are raised with specified description? - Software Engineering Stack Exchange 9000: 提到 library/module 要有 umbrella exception class 的概念 #ril
可供參考的實作:
- requests/exceptions.py at master · requests/requests
- pylint/exceptions.py at master · PyCQA/pylint
- sqlalchemy/exc.py at master · zzzeek/sqlalchemy #ril
- boto3/exceptions.py at develop · boto/boto3
- Search · filename:exceptions.py GitHub 上面好多
exceptions.py裡一起宣告不同的 exceptions,形成一個 hierarchy。 - mkdocs/exceptions.py at master · mkdocs/mkdocs
- scrapy/exceptions.py at master · scrapy/scrapy
-
User-defined Exceptions - 8. Errors and Exceptions — Python 3.7.1 documentation
-
Programs may name their own exceptions by creating a new exception class (see Classes for more about Python classes). Exceptions should typically be derived from the
Exceptionclass, either directly or indirectly.Exception classes can be defined which do anything any other class can do, but are usually KEPT SIMPLE, often only offering a number of ATTRIBUTES that allow INFORMATION ABOUT THE ERROR to be extracted by handlers for the exception.
-
When creating a module that can raise several distinct errors, a common practice is to create A BASE CLASS FOR EXCEPTIONS DEFINED BY THAT MODULE, and subclass that to create specific exception classes for different error conditions:
class Error(Exception): """Base class for exceptions in this module.""" pass class InputError(Error): """Exception raised for errors in the input. Attributes: expression -- input expression in which the error occurred message -- explanation of the error """ def __init__(self, expression, message): self.expression = expression self.message = message class TransitionError(Error): """Raised when an operation attempts a state transition that's not allowed. Attributes: previous -- state at beginning of transition next -- attempted new state message -- explanation of why the specific transition is not allowed """ def __init__(self, previous, next, message): self.previous = previous self.next = next self.message = messageMost exceptions are defined with names that end in
Error, similar to the naming of the standard exceptions.確實,在 Exception hierarchy 裡除了
BaseException與Exception外,所有 exception 的命名都以Error或Warning結尾。其中 base error 就叫做Error,剛好 Exception Hierarchy 裡沒有用到Error。沒有特別的要求? 從範例看來 attribute 確實很多樣,其中
messageattribute 是通用的? 不用考慮Exception的__init__()?? 也不用實作__str__()?? -
Many standard modules define THEIR OWN EXCEPTIONS to report errors that may occur in functions they define. More information on classes is presented in chapter Classes.
Module 就該定義自己的 exception 了,不一定要到 package。
-
-
Handling Exceptions - 8. Errors and Exceptions — Python 3.7.3 documentation
-
The
exceptclause may specify a variable after the exception name. The variable is bound to an exception instance with the arguments stored ininstance.args.For convenience, the exception instance defines
__str__()so the arguments can be printed directly without having to reference.args.">>> try: ... raise Exception('spam', 'eggs') ... except Exception as inst: ... print(type(inst)) # the exception instance ... print(inst.args) # arguments stored in .args ... print(inst) # __str__ allows args to be printed directly, ... # but may be overridden in exception subclasses ... x, y = inst.args # unpack args ... print('x =', x) ... print('y =', y) ... <class 'Exception'> ('spam', 'eggs') ('spam', 'eggs') x = spam y = eggs這說明了 built-in exception 一定會有
.args,且__str__()預設的行為就會把 arguments 全部印出。
-
-
User-defined Exceptions - 8. Errors and Exceptions — Python 2.7.15 documentation 說法跟 Python 3 的文件有些差異
-
Programs may name their own exceptions by creating a new exception class (see Classes for more about Python classes). Exceptions should typically be derived from the
Exceptionclass, either directly or indirectly. For example:>>> class MyError(Exception): ... def __init__(self, value): ... self.value = value ... def __str__(self): ... return repr(self.value) ... >>> try: ... raise MyError(2*2) ... except MyError as e: ... print 'My exception occurred, value:', e.value ... My exception occurred, value: 4 >>> raise MyError('oops!') Traceback (most recent call last): File "<stdin>", line 1, in <module> __main__.MyError: 'oops!'In this example, the default
__init__()ofExceptionhas been overridden. The new behavior simply creates thevalueattribute. This replaces the default behavior of creating theargsattribute.另外也實作了
__str_(),為什麼都不考慮呼叫Exception.__init__()??
-
-
PEP 8 -- Style Guide for Python Code | Python.org
Exception Names
-
Because exceptions should be classes, the class naming convention applies here. However, you should use the suffix
Erroron your exception names (IF the exception actually is an error).這裡埋下了 exception 不一定是 error 的伏筆。
Programming Recommendations
-
Derive exceptions from
Exceptionrather thanBaseException. Direct inheritance fromBaseExceptionis reserved for exceptions where catching them is almost always the wrong thing to do.BaseException不該被 cache? 猜想是針對它直屬 subclass 的說法 -- 包括SystemExit、KeyboardInterrupt、GeneratorExit跟Exception,也難怪except Exception的用法會被 Pylint 警告broad-except。 -
Design exception hierarchies BASED ON THE DISTINCTIONS THAT CODE CATCHING THE EXCEPTIONS IS LIKELY TO NEED, rather than the locations where the exceptions are raised. Aim to answer the question "WHAT WENT WRONG?" programmatically, rather than only stating that "A problem occurred" (see PEP 3151 for an example of this lesson being learned for the builtin exception hierarchy)
-
Class naming conventions apply here, although you should add the suffix
Errorto your exception classes if the exception is an error. Non-error exceptions that are used for NON-LOCAL FLOW CONTROL or other forms of signaling need no special suffix.原來有 non-error exception 這種用法 ??
-
-
Getting Useful Information from an Exception - HandlingExceptions - Python Wiki
-
The
.argsattribute of exceptions is a tuple of all the arguments that were passed in (typically the one and only argument is the error message). This way you can modify the arguments and re-raise, and the extra information will be displayed. You could also put a print statement or logging in theexceptblock.try: a, b, c = d except Exception as e: e.args += (d,) raise這裡改動
args再reraise的做法真怪!? -
Note that not all exceptions subclass
Exception(though almost all do), so this might not catch some exceptions;also, exceptions aren't required to have an
.argsattribute (though it will if the exception subclassesExceptionand DOESN'T OVERRIDE__init__WITHOUT CALLING ITS SUPERCLASS), so the code as written might fail but in practice it almost never does (and if it does, you should fix the non-conformant exception!)說明了自訂 exception 應該要呼叫
Exception.__init__(),雖然不一定有.args,但應該要有的。
-
-
exception Exception - Built-in Exceptions — Python 3.7.1 documentation
-
All built-in, NON-SYSTEM-EXITING exceptions are derived from this class. All user-defined exceptions should also be derived from this class.
從 non-system-exiting 這個定位,可以理解為何自訂的 exception 都必須繼承自它。
-
-
requests/exceptions.py at master · requests/requests
- 所有的 exception/warning 分別繼承自
RequestException(IOError)與RequestsWarning(Warning)。 RequestException的 constructor 採__init__(self, *args, **kwargs),中間會用kwargs.pop('xxx', None)把部份的 keyword arguments 取出,最後會呼叫super(RequestException, self).__init__(*args, **kwargs)MissingSchema(RequestException, ValueError)除了RequestException外還繼承其他 class 的狀況並不少見。
- 所有的 exception/warning 分別繼承自
-
sqlalchemy/exc.py at master · zzzeek/sqlalchemy
- 所有的 exception 都繼承自
SQLAlchemyError(Exception)。 SQLAlchemyError的 constructor 採__init__(self, *arg, **kw),中間會用kw.pop('xxx', None)把部份的 keyword argument 取出,最後會呼叫super(SQLAlchemyError, self).__init__(*arg, **kw),還自訂__str__(self)(會操作到Exception.args),會額外附加 error code 及對應的文件 URL。
- 所有的 exception 都繼承自
-
ValidationError - wtforms/validators.py at master · wtforms/wtforms
ValidationError(ValueError)的__init__(self, message="", *args, **kwargs)單純呼叫ValueError.__init__(self, message, *args, **kwargs)因為第一個 positional argument 是 message?? -
Subclassing Exceptions and Other Fancy Things - Writing and Using Custom Exceptions in Python | Codementor (2015-05-26)
- 這裡也出現
Exception.__init__(self,*args,**kwargs)的寫法。 Exception.__init__(self,"my exception was raised with arguments {0}".format(dErrArguments))感覺對Exception.__init__()的第一個參數做了假設?- The fact that it is
anOhMyGoodnessExcdoesn't matter much─what we care about is the message. 事先定義了不同的oh_my_goodness = Exception("well, that rather badly didnt it?"),視不同的情況再 raise ... 有點特別的用法。
- 這裡也出現
-
How to Define Custom Exceptions in Python? (With Examples)
- using the
raisestatement with an optional error message. 也對Exception的第一個參數做了假設。 - it is a good practice to place all the user-defined exceptions that our program raises in a separate file. Many standard modules do this. They define their exceptions separately as
exceptions.pyorerrors.py(generally but not always). 像 Requests 套件就是。
- using the
-
Problems with user defined exception classes - PYRO - Errors and Troubleshooting
__init__(self, mymessage)在 Python 2.5 後會有問題,要改成__init__(self, *args)或__init__(self, mymessage=None)- If you don't need any special behavior of your own exception objects, it is probably best to just subclass them from
Exceptionand not define any custom methods or properties. That avoids the problem as well. All builtin exceptions should accept a STRING ARGUMENT to be used as the EXCEPTION MESSAGE, so there is no need to subclass the exception and add custom behavior as shown above, if you just want to remember a message string. 問題是所有 built-in exception 都是這樣嗎?
-
The definitive guide to Python exceptions (2016-08-11)
-
從 CPython 的原始碼觀察到,
Exception只是單純地繼承BaseException,沒有加其他東西,而BaseException.__init__的 signature 為BaseException.__init__(*args),只是將傳入的 arguments 存到argsattribute,而這個argsattribute 只被用在BaseException.__str__(),演算法大概像是:def __str__(self): # 專注在 message,不用輸出 type if len(self.args) == 0: return "" if len(self.args) == 1: return str(self.args[0]) return str(self.args) -
所以有了 "the message to display for an exception should be passed as the FIRST AND THE ONLY ARGUMENT to the
BaseException.__init__method" 這樣的結論 => always callBaseException.__init__with only one argument. 下面的範例是看過最棒的,msg=None跟super(CarError, self).__init__(msg)的設計更是巧妙!! 事先把 message 準備好傳入Exception.__init__(),就不用再自訂__str__():class CarError(Exception): """Basic exception for errors raised by cars""" def __init__(self, car, msg=None): if msg is None: # Set some default useful error message msg = "An error occured with car %s" % car super(CarError, self).__init__(msg) self.car = car class CarCrashError(CarError): """When you drive too fast""" def __init__(self, car, other_car, speed): super(CarCrashError, self).__init__( car, msg="Car crashed into %s at speed %d" % (other_car, speed)) self.speed = speed self.other_car = other_car -
Inherits from builtin exceptions types when it makes sense. This makes it easier for programs to NOT BE SPECIFIC TO YOUR APPLICATION OR LIBRARY: 例如
class InvalidColor(CarError, ValueError)=> That allows many programs to catch errors in a more generic way without noticing your own defined type. 這樣的用法也出現在 Requests 裡,例如MissingSchema(RequestException, ValueError)。
-
-
Python exception - how does the args attribute get automatically set? - Stack Overflow #ril
-
Proper way to declare custom exceptions in modern Python? - Stack Overflow #ril
-
Custom Python Exceptions with Error Codes and Error Messages - Stack Overflow #ril
- Programming Recommendations - PEP 8 -- Style Guide for Python Code | Python.org 提到 NON-ERROR EXCEPTIONS that are used for NON-LOCAL FLOW CONTROL or other forms of signaling need no special suffix. 原來還有 non-error exception 這種東西。
- Are exceptions for flow control best practice in Python? - Software Engineering Stack Exchange #ril
- Python. Good, bad, evil -3-: Flow control exceptions - Blog - Open Source - schlitt.info #ril
- Dont Use Exceptions For Flow Control 有提到 Python 的觀點 #ril
- Using Exception Handling for Control Flow (in Python) – Scott Lobdell (2015-06-23) #ril
- try catch - Python using exceptions for control flow considered bad? - Stack Overflow #ril
try:
# ...
except Exception:
_logger.exception('Failed to ...')
- 在 outmost level 做為 uncaught exception handler 時會使用。
- 處理 3rd-party library 時可能會用,尤其無法掌握它會以什麼形式出錯時。
except Exception:的寫法,只比except:(bare except) 少處理了 system-exiting exceptions (SystemExit、KeyboardInterrupt跟GeneratorExit),用 bare except 可能會導致 script 無法結束。
參考資料:
- Programming Recommendations - PEP 8 -- Style Guide for Python Code | Python.org 提到 base
except的使用時機 #ril - W0703 - PyLint Messages
- 抓
Exception時會提示Catching too general exception %s。 - Catching exceptions should be as precise as possible. The type of exceptions that can be raised should be known in advance. Using a catch-all Exception instance defeats the purpose of knowing the type of error that occured, and prohibits the use of tailored responses. 但前提是你有打算處理,以提供 tailored responses。
- 抓
- python - About catching ANY exception - Stack Overflow
- Tim Pietzcker: 你可以,但你不應該。
- Blaze: 重點是少用 (sparingly),尤其在處理 3rd-party library 時,你不會希望有漏網之魚 -- have a backup catch all for the ones you miss。
- Duncan: 只有在 the most outer level of your code 處理 uncaught exception 時才會用。比起 bare except (
except:),except Exception的好處是不會抓KeyboardInterrupt、SystemExit等,不會讓人無法中斷 script。
- Handling Exceptions - 8. Errors and Exceptions — Python 3.7.0 documentation The last except clause may omit the exception name(s), to serve as a wildcard. Use this with extreme caution, since it is easy to mask a real programming error in this way! 不過若是擔心 programming error,測試應該是可以解決的。
- 5. Built-in Exceptions — Python 3.7.0 documentation
- 所有的 exception 都直接或間接繼承自
BaseException,不過 user-defined exception 應該要繼承自Exception,而非BaseException。從Exceptionclass 的說明看來,BaseException是保留給 system-exiting exceptions 使用的。 - 從 Exception hierarchy 看來,做
except Exception:也不過漏了SystemExit、KeyboardInterrupt跟GeneratorExit而已,跟 bare except (except:) 的差別不大。
- 所有的 exception 都直接或間接繼承自
- Logging uncaught exceptions in Python - Stack Overflow #ril
sys.excepthook(type, value, traceback)- sys — System-specific parameters and functions — Python 3.7.4rc1 documentation #ril
手冊: