私は、Python で次のようなコードを書くことがある。
import datetime
def validate_date_string(s):
try:
assert len(s) == 8
datetime.date(int(s[:4]), int(s[4:6]), int(s[6:]))
return True
except StandardError:
return False
def test():
assert validate_date_string('20120229') # leap year
assert not validate_date_string('20130229') # not leap year
StandardError を捕捉するのは、Google 先生の教えによる。
- Never use catch-all except: statements, or catch Exception or StandardError, unless you are re-raising the exception or in the outermost block in your thread (and printing an error message). Python is very tolerant in this regard and except: will really catch everything including misspelled names, sys.exit() calls, Ctrl+C interrupts, unittest failures and all kinds of other exceptions that you simply don't want to catch.
しかしここには、Python のバージョンによって例外のクラス階層が異なる、という重要な情報が抜けている。
実際、Python 2.4 以前は KeyboardInterruput が StarndardError のサブクラスになっているため、上記のコードは try ブロック中で Ctrl-C を受けると意図通りに動かない。具体的には、Ctrl-C で終了できず、無条件に False が返ってしまう。
Exception
+-- SystemExit
+-- StopIteration
+-- StandardError
| +-- KeyboardInterrupt
<...snip...>
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- Exception
+-- GeneratorExit
+-- StopIteration
+-- StandardError
<...snip...>
Python 2.4 以前でも動くようにするには、StandardError を処理する 前 に、KeyboardInterruput を明示的に処理する必要がある。
def validate_date_string(s):
try:
assert len(s) == 8
datetime.date(int(s[:4]), int(s[4:6]), int(s[6:]))
return True
except KeyboardInterrupt:
raise
except StandardError:
return False
Python は保守的な言語だと思っていたので、まさかこんな所に罠があるとは思わなかった。これでまた 1 つ、Python が嫌いになった。:-p