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: division 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: can only concatenate str (not "int") to str

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

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

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

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


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

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

   >>> while True:
   ...     try:
   ...         x = int(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 ブロックの後ろへ実行が継続
  されます。

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

一つの "try" 文には複数の *except 節* が付けられ、別々の例外に対するハ
ンドラを指定できます。 多くとも一つのハンドラしか実行されません。 ハン
ドラは対応する *try 節* 内で発生した例外だけを処理し、同じ "try" 節内
の別の例外ハンドラで起きた例外は処理しません。 *except 節* では丸括弧
で囲ったタプルという形で複数の例外を指定できます。例えば次のようにしま
す:

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

"except" 節のクラスは、例外と同じクラスか基底クラスのときに互換
(compatible)となります。 (逆方向では成り立ちません --- 派生クラスの例
外がリストされている except 節は基底クラスの例外と互換ではありません)
。例えば、次のコードは、 B, C, D を順序通りに出力します:

   class B(Exception):
       pass

   class C(B):
       pass

   class D(C):
       pass

   for cls in [B, C, D]:
       try:
           raise cls()
       except D:
           print("D")
       except C:
           print("C")
       except B:
           print("B")

*except 節* が逆に並んでいた場合 ("except B" が最初にくる場合)、 B, B,
B と出力されるはずだったことに注意してください --- 最初に一致した
*except 節* が駆動されるのです。

All exceptions inherit from "BaseException", and so it can be used to
serve as a wildcard. Use this with extreme caution, since it is easy
to mask a real programming error in this way!  It can also be used to
print an error message and then re-raise the exception (allowing a
caller to handle the exception as well):

   import sys

   try:
       f = open('myfile.txt')
       s = f.readline()
       i = int(s.strip())
   except OSError as err:
       print("OS error: {0}".format(err))
   except ValueError:
       print("Could not convert data to an integer.")
   except BaseException as err:
       print(f"Unexpected {err=}, {type(err)=}")
       raise

Alternatively the last except clause may omit the exception name(s),
however the exception value must then be retrieved from
"sys.exc_info()[1]".

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

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

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

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

The *except clause* may specify a variable after the exception name.
The variable is bound to an exception instance with the arguments
stored in "instance.args".  For convenience, the exception instance
defines "__str__()" so the arguments can be printed directly without
having to reference ".args".  One may also instantiate an exception
first before raising it and add any attributes to it as desired.

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

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

Exception handlers don't just handle exceptions if they occur
immediately in the *try clause*, but also if they occur inside
functions that are called (even indirectly) in the *try clause*. For
example:

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


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

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

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

"raise" の唯一の引数は送出される例外を指し示します。 これは例外インス
タンスか例外クラス ("Exception" を継承したクラス) でなければなりません
。 例外クラスが渡された場合は、引数無しのコンストラクタが呼び出され、
暗黙的にインスタンス化されます:

   raise ValueError  # shorthand for 'raise ValueError()'

例外が発生したかどうかを判定したいだけで、その例外を処理するつもりがな
ければ、単純な形式の "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. 例外の連鎖
===============

"except" 節の中で未処理の例外が発生した場合、その未処理の例外は処理さ
れた例外のエラーメッセージに含まれます:

   >>> try:
   ...     open("database.sqlite")
   ... except OSError:
   ...     raise RuntimeError("unable to handle error")
   ...
   Traceback (most recent call last):
     File "<stdin>", line 2, in <module>
   FileNotFoundError: [Errno 2] No such file or directory: 'database.sqlite'

   During handling of the above exception, another exception occurred:

   Traceback (most recent call last):
     File "<stdin>", line 4, in <module>
   RuntimeError: unable to handle error

ある例外が他の例外から直接影響されていることを示すために、"raise" 文に
オプションの "from" 句を指定します:

   # exc must be exception instance or None.
   raise RuntimeError from exc

これは例外を変換するときに便利です。例えば:

   >>> def func():
   ...     raise ConnectionError
   ...
   >>> try:
   ...     func()
   ... except ConnectionError as exc:
   ...     raise RuntimeError('Failed to open database') from exc
   ...
   Traceback (most recent call last):
     File "<stdin>", line 2, in <module>
     File "<stdin>", line 2, in func
   ConnectionError

   The above exception was the direct cause of the following exception:

   Traceback (most recent call last):
     File "<stdin>", line 4, in <module>
   RuntimeError: Failed to open database

また、自動的な例外の連鎖を無効にするには "from None" を指定します。

   >>> try:
   ...     open('database.sqlite')
   ... except OSError:
   ...     raise RuntimeError from None
   ...
   Traceback (most recent call last):
     File "<stdin>", line 4, in <module>
   RuntimeError

例外の連鎖の仕組みに関して、詳しくは 組み込み例外 を参照してください。


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

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

例外クラスでは、普通のクラスができることなら何でも定義することができま
すが、通常は単純なものにしておきます。大抵は、いくつかの属性だけを提供
し、例外が発生したときにハンドラがエラーに関する情報を取り出せるように
する程度にとどめます。

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

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


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

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

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

もし "finally" 節がある場合、 "try" 文が終わる前の最後の処理を、
"finally" 節が実行します。 "try" 文が例外を発生させるか否かに関わらず
、 "finally" 節は実行されます。以下では、例外が発生するという更に複雑
なケースを議論します:

* もし "try" 文の実行中に例外が発生したら、その例外は "except" 節によ
  って処理されるでしょう。もしその例外が "except" 節によって処理されな
  ければ、 "finally" 節が実行された後に、その例外が再送出されます。

* "except" 節または "else" 節の実行中に例外が発生することがあり得ます
  。その場合も、 "finally" 節が実行された後に例外が再送出されます。

* "finally" 節で "break"、"continue" または "return" 文が実行された場
  合、例外は再送出されません。

* もし "try" 文が "break" 文、 "continue" 文または "return" 文のいずれ
  かに達すると、その "break" 文、 "continue" 文または "return" 文の実
  行の直前に "finally" 節が実行されます。

* もし "finally" 節が "return" 文を含む場合、返される値は "try" 節の
  "return" 文ではなく、"finally" 節の "return" 文によるものになります
  。

例えば:

   >>> def bool_return():
   ...     try:
   ...         return True
   ...     finally:
   ...         return False
   ...
   >>> bool_return()
   False

より複雑な例:

   >>> 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.0
   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.8. 定義済みクリーンアップ処理
===============================

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

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

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

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

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