例外 ========================================================================================== 一部の型チェック動作(型の絞り込みや到達可能性の分析など)では、型チェッカーがコードフローを理解する必要があります。 コードフローは通常、ある文から次の文へと進行しますが、``for``、``while``、``return`` などの一部の文はコードフローを変更することがあります。 同様に、``try``/``except``/``finally`` 文はコードフローに影響を与えるため、型評価にも影響を与える可能性があります。 例:: x = None try: some_function() x = 1 except NotImplementedError: pass # この時点での `x` の型は、`some_function` が例外を発生させた場合は None であり、発生させなかった場合は `Literal[1]` である可能性があるため、型チェッカーはこの分析に基づいてその型を絞り込むことを選択する場合があります。 reveal_type(x) # Literal[1] | None コンテキストマネージャー ------------------------------------------------------------------------------------------ コンテキストマネージャーは、``__exit__`` メソッドから ``True``(または他の真値)を返すことで、例外を「抑制」することができます。 このようなコンテキストマネージャーが使用される場合、``with`` ブロック内で発生し、他にキャッチされない例外はコンテキストマネージャーによってキャッチされ、制御は ``with`` ブロックの直後に続きます。 コンテキストマネージャーが例外を抑制しない場合(通常はそうです)、``with`` ブロック内で発生し、他にキャッチされない例外は ``with`` ブロックを超えて伝播します。 コードフロー分析を行う型チェッカーは、これらの 2 つのケースを区別できる必要があります。 これは、コンテキストマネージャーの ``__exit__`` メソッドの戻り型注釈を調べることで行われます。 ``__exit__`` メソッドの戻り型が特に ``bool`` または ``Literal[True]`` である場合、型チェッカーは例外が抑制される*可能性がある*と仮定する必要があります。 他の戻り型の場合、型チェッカーは例外が抑制され*ない*と仮定する必要があります。 例としては、``Any``、``Literal[False]``、``None``、および ``bool | None`` があります。 この慣習は、ほとんどのコンテキストマネージャーが例外を抑制しないため選ばれました。 そして、彼らの ``__exit__`` メソッドは ``bool | None`` を返すように注釈されることが一般的です。 例外を抑制するコンテキストマネージャーは比較的まれであり、特別なケースと見なされます。 たとえば、次のコンテキストマネージャーは例外を抑制します:: class Suppress: def __enter__(self) -> None: pass def __exit__(self, exc_type, exc_value, traceback) -> bool: return True with Suppress(): raise ValueError("この例外は抑制されます") # 例外が抑制されるため、この行は到達可能です。 print("コードは到達可能です")