8. エラーと例外
***************

これまでエラーメッセージについては簡単に触れるだけでしたが、チュートリ
アル中の例を自分で試していたら、実際にいくつかのエラーメッセージを見て
いることでしょう。エラーには (少なくとも) 二つのはっきり異なる種類があ
ります。それは *構文エラー (syntax error)* と *例外 (exception)* です
。


8.1. 構文エラー
===============

構文エラーは構文解析エラー (parsing error) としても知られており、
Python を勉強している間に最もよく遭遇する問題の一つでしょう:

   >>> while True print 'Hello world'
     File "<stdin>", line 1
       while True print 'Hello world'
                      ^
   SyntaxError: invalid syntax

パーサは違反の起きている行を表示し、小さな「矢印」を表示して、行中でエ
ラーが検出された最初の位置を示します。エラーは矢印の *直前の* トークン
でひき起こされています (または、少なくともそこで検出されています)。上
記の例では、エラーは "print" で検出されています。コロン ("':'") がその
前に無いからです。入力がスクリプトから来ている場合は、どこを見ればよい
か分かるようにファイル名と行番号が出力されます。


8.2. 例外
=========

たとえ文や式が構文的に正しくても、実行しようとしたときにエラーが発生す
るかもしれません。実行中に検出されたエラーは *例外 (exception)* と呼ば
れ、常に致命的とは限りません。これから、Python プログラムで例外をどの
ように扱うかを学んでいきます。ほとんどの例外はプログラムで処理されず、
以下に示されるようなメッセージになります:

   >>> 10 * (1/0)
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   ZeroDivisionError: integer division or modulo by zero
   >>> 4 + spam*3
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   NameError: name 'spam' is not defined
   >>> '2' + 2
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   TypeError: cannot concatenate 'str' and 'int' objects

エラーメッセージの最終行は何が起こったかを示しています。例外は様々な型
(type) で起こり、その型がエラーメッセージの一部として出力されます。上
の例での型は "ZeroDivisionError", "NameError", "TypeError" です。例外
型として出力される文字列は、発生した例外の組み込み名です。これは全ての
組み込み例外について成り立ちますが、ユーザ定義の例外では (成り立つよう
にするのは有意義な慣習ですが) 必ずしも成り立ちません。標準例外の名前は
組み込みの識別子です (予約語ではありません)。

残りの行は例外の詳細で、その例外の型と何が起きたかに依存します。

エラーメッセージの先頭部分では、例外が発生した実行コンテキスト
(context) を、スタックのトレースバック (stack traceback) の形式で示し
ています。一般には、この部分にはソースコード行をリストしたトレースバッ
クが表示されます。しかし、標準入力から読み取られたコードは表示されませ
ん。

組み込み例外 には、組み込み例外とその意味がリストされています。


8.3. 例外を処理する
===================

例外を選別して処理するようなプログラムを書くことができます。以下の例を
見てください。この例では、有効な文字列が入力されるまでユーザに入力を促
しますが、ユーザがプログラムに ("Control-C" か、またはオペレーティング
システムがサポートしている何らかのキーを使って) 割り込みをかけてプログ
ラムを中断させることができるようにしています。ユーザが生成した割り込み
は、 "KeyboardInterrupt" 例外が送出されることで通知されるということに
注意してください。

   >>> while True:
   ...     try:
   ...         x = int(raw_input("Please enter a number: "))
   ...         break
   ...     except ValueError:
   ...         print "Oops!  That was no valid number.  Try again..."
   ...

"try" 文は下記のように動作します。

* まず、 *try 節 (try clause)* (キーワード "try" と "except" の間の
  文) が実行されます。

* 何も例外が発生しなければ、 *except 節* をスキップして "try" 文の実
  行 を終えます。

* try 節内の実行中に例外が発生すると、その節の残りは飛ばされます。次
  に 、例外型が "except" キーワードの後に指定されている例外に一致する
  場合 、except 節が実行された後、 "try" 文の後ろへ実行が継続されます
  。

* もしも except 節で指定された例外と一致しない例外が発生すると、その
  例 外は "try" 文の外側に渡されます。例外に対するハンドラ (handler、
  処理 部) がどこにもなければ、 *処理されない例外 (unhandled
  exception)* と なり、上記に示したようなメッセージを出して実行を停止
  します。

一つの "try" 文に複数の except 節を設けて、さまざまな例外に対するハン
ドラを指定することができます。同時に一つ以上のハンドラが実行されること
はありません。ハンドラは対応する try 節内で発生した例外だけを処理し、
同じ "try" 文内の別の例外ハンドラで起きた例外は処理しません。 except
節には複数の例外を丸括弧で囲ったタプルにして渡すことができます。例えば
以下のようにします:

   ... except (RuntimeError, TypeError, NameError):
   ...     pass

このタプルの周囲の括弧は必要です。というのも、 "except ValueError, e:"
はモダンな Python で普通 "except ValueError as e:" と書く(後述)のと同
じ意味のかつての文法でした。この古い文法は後方互換のために今でも残され
ています。つまり "except RuntimeError, TypeError" は "except
(RuntimeError, TypeError):" と同じではなく、お望みのものとは違う
"except RuntimeError as TypeError:" だということです。

最後の except 節では例外名を省いて、ワイルドカード (wildcard、総称記号
) にすることができます。ワイルドカードの except 節は非常に注意して使っ
てください。というのは、ワイルドカードは通常のプログラムエラーをたやす
く隠してしまうからです！ワイルドカードの except 節はエラーメッセージを
出力した後に例外を再送出する (関数やメソッドの呼び出し側が同様にして例
外を処理できるようにする) 用途にも使えます:

   import sys

   try:
       f = open('myfile.txt')
       s = f.readline()
       i = int(s.strip())
   except IOError as e:
       print "I/O error({0}): {1}".format(e.errno, e.strerror)
   except ValueError:
       print "Could not convert data to an integer."
   except:
       print "Unexpected error:", sys.exc_info()[0]
       raise

"try" ... "except" 文には、オプションで *else 節 (else clause)* を設け
ることができます。 "else" 節を設ける場合、全ての "except" 節よりも後ろ
に置かなければなりません。 "else" 節は try 節で全く例外が送出されなか
ったときに実行されるコードを書くのに役立ちます。例えば次のようにします
:

   for arg in sys.argv[1:]:
       try:
           f = open(arg, 'r')
       except IOError:
           print 'cannot open', arg
       else:
           print arg, 'has', len(f.readlines()), 'lines'
           f.close()

追加のコードを追加するのは "try" 節の後ろよりも "else" 節の方がよいで
しょう。なぜなら、そうすることで "try" ... "except" 文で保護したいコー
ドから送出されたもの以外の例外を偶然に捕捉してしまうという事態を避けら
れるからです。

例外が発生するとき、例外は関連付けられた値を持つことができます。この値
は例外の *引数 (argument)* とも呼ばれます。引数の有無および引数の型は
、例外の型に依存します。

except 節では、例外名 (または例外名タプル) の後に変数を指定することが
できます。この変数は例外インスタンスに結び付けられており、
"instance.args" に例外インスタンス生成時の引数が入っています。例外イン
スタンスには "__str__()" が定義されており、 ".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
   ...     x, y = inst.args
   ...     print 'x =', x
   ...     print 'y =', y
   ...
   <type 'exceptions.Exception'>
   ('spam', 'eggs')
   ('spam', 'eggs')
   x = spam
   y = eggs

例外が引数を持っていれば、それらは処理されない例外のメッセージの最後の
部分 (「詳細説明」) に出力されます。

例外ハンドラは、try 節の直接内側で発生した例外を処理するだけではなく、
その try 節から (たとえ間接的にでも) 呼び出された関数の内部で発生した
例外も処理します。例えば:

   >>> def this_fails():
   ...     x = 1/0
   ...
   >>> try:
   ...     this_fails()
   ... except ZeroDivisionError as detail:
   ...     print 'Handling run-time error:', detail
   ...
   Handling run-time error: integer division or modulo by zero


8.4. 例外を送出する
===================

"raise" 文を使って、特定の例外を発生させることができます。例えば:

   >>> raise NameError('HiThere')
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
   NameError: HiThere

"raise" は送出したい例外を引数として取ります。これは例外クラス
("Exception" を継承したクラス)か、例外クラスのインスタンスです。

例外が発生したかどうかを判定したいだけで、その例外を処理するつもりがな
ければ、単純な形式の "raise" 文を使って例外を再送出させることができま
す:

   >>> try:
   ...     raise NameError('HiThere')
   ... except NameError:
   ...     print 'An exception flew by!'
   ...     raise
   ...
   An exception flew by!
   Traceback (most recent call last):
     File "<stdin>", line 2, in <module>
   NameError: HiThere


8.5. ユーザー定義例外
=====================

プログラム上で新しい例外クラスを作成することで、独自の例外を指定するこ
とができます (Python のクラスについては クラス 参照)。例外は、典型的に
"Exception" クラスから、直接または間接的に派生したものです。例を示しま
す:

   >>> 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!'

この例では "Exception" のデフォルト "__init__()" がオーバーライドされ
ています。新しい振る舞いでは、単に *value* 属性を作ります。これは、デ
フォルトの *args* 属性を作成する振る舞いを置き換えています。

例外クラスでは、普通のクラスができることなら何でも定義することができま
すが、通常は単純なものにしておきます。大抵は、いくつかの属性だけを提供
し、例外が発生したときにハンドラがエラーに関する情報を取り出せるように
する程度にとどめます。複数の別個の例外を送出するようなモジュールを作成
する際には、そのモジュールで定義されている例外の基底クラスを作成するの
が一般的なプラクティスです:

   class Error(Exception):
       """Base class for exceptions in this module."""
       pass

   class InputError(Error):
       """Exception raised for errors in the input.

       Attributes:
           expr -- input expression in which the error occurred
           msg  -- explanation of the error
       """

       def __init__(self, expr, msg):
           self.expr = expr
           self.msg = msg

   class TransitionError(Error):
       """Raised when an operation attempts a state transition that's not
       allowed.

       Attributes:
           prev -- state at beginning of transition
           next -- attempted new state
           msg  -- explanation of why the specific transition is not allowed
       """

       def __init__(self, prev, next, msg):
           self.prev = prev
           self.next = next
           self.msg = msg

ほとんどの例外は、標準の例外の名前付けと同様に、"Error" で終わる名前で
定義されています。

多くの標準モジュールでは、モジュールで定義されている関数内で発生する可
能性のあるエラーを報告させるために、独自の例外を定義しています。クラス
についての詳細な情報は クラス 章で提供されています。


8.6. クリーンアップ動作を定義する
=================================

"try" 文にはもう一つオプションの節があります。この節はクリーンアップ動
作を定義するためのもので、どんな状況でも必ず実行されます。例を示します
:

   >>> try:
   ...     raise KeyboardInterrupt
   ... finally:
   ...     print 'Goodbye, world!'
   ...
   Goodbye, world!
   Traceback (most recent call last):
     File "<stdin>", line 2, in <module>
   KeyboardInterrupt

*finally 節 (finally clause)* は、例外が発生したかどうかに関わらず、
"try" 文を抜ける前に常に実行されます。 "try" 節の中で例外が発生して、
"except" 節で処理されていない場合、または "except" 節か "else" 節で例
外が発生した場合は、 "finally" 節を実行した後、その例外を再送出します
。 "finally" 節はまた、 "try" 節から "break" 文や "continue" 文、
"return" 文経由で抜ける際にも、 "抜ける途中で" 実行されます。より複雑
な例です ("except" 節や "finally" 節が同じ "try" 文の中にあって、
Python 2.5 以降で動作します)。

   >>> def divide(x, y):
   ...     try:
   ...         result = x / y
   ...     except ZeroDivisionError:
   ...         print "division by zero!"
   ...     else:
   ...         print "result is", result
   ...     finally:
   ...         print "executing finally clause"
   ...
   >>> divide(2, 1)
   result is 2
   executing finally clause
   >>> divide(2, 0)
   division by zero!
   executing finally clause
   >>> divide("2", "1")
   executing finally clause
   Traceback (most recent call last):
     File "<stdin>", line 1, in <module>
     File "<stdin>", line 3, in divide
   TypeError: unsupported operand type(s) for /: 'str' and 'str'

見てわかるとおり、 "finally" 節はどの場合にも実行されています。文字列
を割り算することで発生した "TypeError" は "except" 節で処理されていな
いので、 "finally" 節実行後に再度送出されています。

実世界のアプリケーションでは、 "finally" 節は(ファイルやネットワーク接
続などの)外部リソースを、利用が成功したかどうかにかかわらず解放するた
めに便利です。


8.7. 定義済みクリーンアップ処理
===============================

オブジェクトのなかには、その利用の成否にかかわらず、不要になった際に実
行される標準的なクリーンアップ処理が定義されているものがあります。以下
の、ファイルをオープンして内容を画面に表示する例をみてください。

   for line in open("myfile.txt"):
       print line,

このコードの問題点は、コードの実行が終わった後に不定の時間ファイルを開
いたままでいることです。これは単純なスクリプトでは問題になりませんが、
大きなアプリケーションでは問題になりえます。 "with" 文はファイルのよう
なオブジェクトが常に、即座に正しくクリーンアップされることを保証します
。

   with open("myfile.txt") as f:
       for line in f:
           print line,

この文が実行されたあとで、たとえ行の処理中に問題があったとしても、ファ
イル *f* は常に close されます。ファイルなどの、定義済みクリーンアップ
処理を持つオブジェクトについては、それぞれのドキュメントで示されます。
