コンテンツへスキップ

例外とエラー

コードにエラーがあり、実行すると、エラーと例外が表示されます。

Typerは、これらのエラーを迅速に検出できるようにするためのいくつかのトリックを行っています。

壊れたアプリの例

この壊れたアプリの例を見てみましょう。

import typer


def main(name: str = "morty"):
    print(name + 3)


if __name__ == "__main__":
    typer.run(main)

このコードは、文字列と数値(name + 3)を合計できないため、壊れています。

Richを使用した例外

Richがインストールされている場合(たとえば、"typer[all]"をインストールした場合)、Typerはそれを使用して、適切に印刷されたエラーを自動的に表示します。

TyperとClickの内部部分から発生するトレースバック(関数を呼び出した一連のもの)のすべての部分を省略します。

そのため、表示されるエラーははるかに明確でシンプルになり、コードの問題を迅速に検出するのに役立ちます。

$ python main.py

<font color="#F92672">╭──────────────── </font><font color="#F92672"><b>Traceback (most recent call last)</b></font><font color="#F92672"> ────────────────╮</font>
<font color="#F92672">│</font> <font color="#A37F4E">/home/user/code/superapp/</font><font color="#F4BF75"><b>main.py</b></font>:<font color="#66D9EF">5</font> in <font color="#A6E22E">main</font>                        <font color="#F92672">│</font>
<font color="#F92672">│</font>                                                                   <font color="#F92672">│</font>
<font color="#F92672">│</font>    2                                                              <font color="#F92672">│</font>
<font color="#F92672">│</font>    3                                                              <font color="#F92672">│</font>
<font color="#F92672">│</font>    4 <font color="#66D9EF">def</font> <font color="#A6E22E">main</font>(name: <font color="#A1EFE4">str</font> = <font color="#F4BF75">&quot;morty&quot;</font>):                               <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F92672">❱ </font> 5 │   <font color="#A1EFE4">print</font>(name + <font color="#66D9EF">3</font>)                                          <font color="#F92672">│</font>
<font color="#F92672">│</font>    6                                                              <font color="#F92672">│</font>
<font color="#F92672">│</font>    7                                                              <font color="#F92672">│</font>
<font color="#F92672">│</font>    8 <font color="#66D9EF">if</font> <font color="#F92672">__name__</font> == <font color="#F4BF75">&quot;__main__&quot;</font>:                                   <font color="#F92672">│</font>
<font color="#F92672">│</font>                                                                   <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75">╭──── locals ────╮</font>                                                <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75">│</font> name = <font color="#F4BF75">&apos;morty&apos;</font> <font color="#F4BF75">│</font>                                                <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75">╰────────────────╯</font>                                                <font color="#F92672">│</font>
<font color="#F92672">╰───────────────────────────────────────────────────────────────────╯</font>
<font color="#F92672"><b>TypeError: </b></font>can only concatenate str <b>(</b>not <font color="#A6E22E">&quot;int&quot;</font><b>)</b> to str

Richを使用しない例外

Richがインストールされていない場合でも、Typerは情報をできるだけ明確に表示するためにいくつかのトリックを行います。

$ python main.py

Traceback (most recent call last):

  File "main.py", line 12, in <module>
    typer.run(main)

  File "main.py", line 8, in main
    print(name + 3)

TypeError: can only concatenate str (not "int") to str

セキュリティのためのローカル変数の無効化

Typerアプリケーションが機密情報、たとえばパスワードキートークンを処理する場合、自動エラーがローカル変数に値を表示すると、問題が発生する可能性があります。

これは特に、CLIアプリケーションがログを記録している一部のCI(継続的インテグレーション)システムで実行されている場合に重要になります。

上記のエラーのデフォルトでは、Richを使用する場合、セクションが表示されます。

name = 'morty'

この場合、nameはローカル変数であり、関数に渡されたパラメータから取得されます。

しかし、それがパスワードのようなものであれば、非表示にしたかったでしょう。

その場合は、typer.Typer()アプリケーションを明示的に作成し、パラメータpretty_exceptions_show_locals=Falseを設定できます。

import typer

app = typer.Typer(pretty_exceptions_show_locals=False)


@app.command()
def main(password: str):
    print(password + 3)


if __name__ == "__main__":
    app()

そして、実行すると、ローカル変数なしでエラーが表示されるようになります。

$ python main.py supersecret

<font color="#F92672">╭──────────────── </font><font color="#F92672"><b>Traceback (most recent call last)</b></font><font color="#F92672"> ────────────────╮</font>
<font color="#F92672">│</font> <font color="#A37F4E">/home/user/code/superapp/</font><font color="#F4BF75"><b>main.py</b></font>:<font color="#66D9EF">8</font> in <font color="#A6E22E">main</font>                        <font color="#F92672">│</font>
<font color="#F92672">│</font>                                                                   <font color="#F92672">│</font>
<font color="#F92672">│</font>    5                                                              <font color="#F92672">│</font>
<font color="#F92672">│</font>    6 <font color="#AE81FF"><b>@app</b></font>.command()                                               <font color="#F92672">│</font>
<font color="#F92672">│</font>    7 <font color="#66D9EF">def</font> <font color="#A6E22E">main</font>(password: <font color="#A1EFE4">str</font>):                                     <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F92672">❱ </font> 8 │   <font color="#A1EFE4">print</font>(password + <font color="#66D9EF">3</font>)                                      <font color="#F92672">│</font>
<font color="#F92672">│</font>    9                                                              <font color="#F92672">│</font>
<font color="#F92672">│</font>   10                                                              <font color="#F92672">│</font>
<font color="#F92672">│</font>   11 <font color="#66D9EF">if</font> <font color="#F92672">__name__</font> == <font color="#F4BF75">&quot;__main__&quot;</font>:                                   <font color="#F92672">│</font>
<font color="#F92672">╰───────────────────────────────────────────────────────────────────╯</font>
<font color="#F92672"><b>TypeError: </b></font>can only concatenate str <b>(</b>not <font color="#A6E22E">&quot;int&quot;</font><b>)</b> to str

パスワードsupersecretを渡しましたが、エラーメッセージにはどこにも表示されていないことに注意してください。

ローカル変数の値を表示できることは、通常、問題の診断、デバッグ、および修正に非常に役立ちますが、機密情報を扱っている場合は、保護する方法がわかりました。🔒

短い出力の無効化

TyperとClickの部分を含む完全な例外を表示する場合は、パラメータpretty_exceptions_short=Falseを使用できます。

import typer

app = typer.Typer(pretty_exceptions_short=False)


@app.command()
def main(name: str = "morty"):
    print(name + 3)


if __name__ == "__main__":
    app()

実行すると、出力全体が表示されるようになります。

$ python main.py

<font color="#F92672">╭──────────────── </font><font color="#F92672"><b>Traceback (most recent call last)</b></font><font color="#F92672"> ────────────────╮</font>
<font color="#F92672">│</font> <font color="#A37F4E">/home/user/code/superapp/</font><font color="#F4BF75"><b>main.py</b></font>:<font color="#66D9EF">12</font> in <font color="#A6E22E">&lt;module&gt;</font>                   <font color="#F92672">│</font>
<font color="#F92672">│</font>                                                                   <font color="#F92672">│</font>
<font color="#F92672">│</font>    9                                                              <font color="#F92672">│</font>
<font color="#F92672">│</font>   10                                                              <font color="#F92672">│</font>
<font color="#F92672">│</font>   11 <font color="#66D9EF">if</font> <font color="#F92672">__name__</font> == <font color="#F4BF75">&quot;__main__&quot;</font>:                                   <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F92672">❱ </font>12 │   app()                                                    <font color="#F92672">│</font>
<font color="#F92672">│</font>   13                                                              <font color="#F92672">│</font>
<font color="#F92672">│</font>                                                                   <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75">╭─────────────────────────── locals ────────────────────────────╮</font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75">│</font> <font color="#A6194C">__annotations__</font> = <b>{}</b>                                          <font color="#F4BF75">│</font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75">│</font>    <font color="#A6194C">__builtins__</font> = <b>&lt;</b><font color="#AE81FF"><b>module</b></font> <font color="#F4BF75">&apos;builtins&apos;</font> <b>(</b>built-in<b>)&gt;</b>              <font color="#F4BF75">│</font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75">│</font>      <font color="#A6194C">__cached__</font> = <font color="#66D9EF">None</font>                                        <font color="#F4BF75">│</font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75">│</font>         <font color="#A6194C">__doc__</font> = <font color="#66D9EF">None</font>                                        <font color="#F4BF75">│</font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75">│</font>        <font color="#A6194C">__file__</font> = <font color="#F4BF75">&apos;main.py&apos;</font>                                   <font color="#F4BF75">│</font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75">│</font>      <font color="#A6194C">__loader__</font> = <b>&lt;</b><font color="#AE81FF"><b>_frozen_importlib_external.SourceFileLoad…</b></font> <font color="#F4BF75">│</font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75">│</font>                   object at <font color="#66D9EF">0x7f047db1c050</font><b>&gt;</b>                   <font color="#F4BF75">│</font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75">│</font>        <font color="#A6194C">__name__</font> = <font color="#F4BF75">&apos;__main__&apos;</font>                                  <font color="#F4BF75">│</font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75">│</font>     <font color="#A6194C">__package__</font> = <font color="#66D9EF">None</font>                                        <font color="#F4BF75">│</font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75">│</font>        <font color="#A6194C">__spec__</font> = <font color="#66D9EF">None</font>                                        <font color="#F4BF75">│</font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75">│</font>             app = <b>&lt;</b><font color="#AE81FF"><b>typer.main.Typer</b></font> object at <font color="#66D9EF">0x7f047db51d90</font><b>&gt;</b> <font color="#F4BF75">│</font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75">│</font>            main = <b>&lt;</b><font color="#AE81FF"><b>function</b></font> main at <font color="#66D9EF">0x7f047db56830</font><b>&gt;</b>           <font color="#F4BF75">│</font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75">│</font>           typer = <b>&lt;</b><font color="#AE81FF"><b>module</b></font> <font color="#F4BF75">&apos;typer&apos;</font> from                        <font color="#F4BF75">│</font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75">│</font>                   <font color="#F4BF75">&apos;/home/user/code/superapp/env/lib/python3.…</font> <font color="#F4BF75">│</font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75">╰───────────────────────────────────────────────────────────────╯</font> <font color="#F92672">│</font>
<font color="#F92672">│</font>                                                                   <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#A37F4E">/home/user/code/superapp/env/lib/python3.7/site-packages/typer/</font><font color="#F4BF75"><b>ma</b></font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75"><b>in.py</b></font>:<font color="#66D9EF">328</font> in <font color="#A6E22E">__call__</font>                                             <font color="#F92672">│</font>
<font color="#F92672">│</font>                                                                   <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#A37F4E">/home/user/code/superapp/env/lib/python3.7/site-packages/typer/</font><font color="#F4BF75"><b>ma</b></font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75"><b>in.py</b></font>:<font color="#66D9EF">311</font> in <font color="#A6E22E">__call__</font>                                             <font color="#F92672">│</font>
<font color="#F92672">│</font>                                                                   <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#A37F4E">/home/user/code/superapp/env/lib/python3.7/site-packages/click/</font><font color="#F4BF75"><b>co</b></font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75"><b>re.py</b></font>:<font color="#66D9EF">1130</font> in <font color="#A6E22E">__call__</font>                                            <font color="#F92672">│</font>
<font color="#F92672">│</font>                                                                   <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#A37F4E">/home/user/code/superapp/env/lib/python3.7/site-packages/typer/</font><font color="#F4BF75"><b>co</b></font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75"><b>re.py</b></font>:<font color="#66D9EF">723</font> in <font color="#A6E22E">main</font>                                                 <font color="#F92672">│</font>
<font color="#F92672">│</font>                                                                   <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#A37F4E">/home/user/code/superapp/env/lib/python3.7/site-packages/typer/</font><font color="#F4BF75"><b>co</b></font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75"><b>re.py</b></font>:<font color="#66D9EF">216</font> in <font color="#A6E22E">_main</font>                                                <font color="#F92672">│</font>
<font color="#F92672">│</font>                                                                   <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#A37F4E">/home/user/code/superapp/env/lib/python3.7/site-packages/click/</font><font color="#F4BF75"><b>co</b></font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75"><b>re.py</b></font>:<font color="#66D9EF">1404</font> in <font color="#A6E22E">invoke</font>                                              <font color="#F92672">│</font>
<font color="#F92672">│</font>                                                                   <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#A37F4E">/home/user/code/superapp/env/lib/python3.7/site-packages/click/</font><font color="#F4BF75"><b>co</b></font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75"><b>re.py</b></font>:<font color="#66D9EF">760</font> in <font color="#A6E22E">invoke</font>                                               <font color="#F92672">│</font>
<font color="#F92672">│</font>                                                                   <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#A37F4E">/home/user/code/superapp/env/lib/python3.7/site-packages/typer/</font><font color="#F4BF75"><b>ma</b></font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75"><b>in.py</b></font>:<font color="#66D9EF">683</font> in <font color="#A6E22E">wrapper</font>                                              <font color="#F92672">│</font>
<font color="#F92672">│</font>                                                                   <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#A37F4E">/home/user/code/superapp/</font><font color="#F4BF75"><b>main.py</b></font>:<font color="#66D9EF">8</font> in <font color="#A6E22E">main</font>                        <font color="#F92672">│</font>
<font color="#F92672">│</font>                                                                   <font color="#F92672">│</font>
<font color="#F92672">│</font>    5                                                              <font color="#F92672">│</font>
<font color="#F92672">│</font>    6 <font color="#AE81FF"><b>@app</b></font>.command()                                               <font color="#F92672">│</font>
<font color="#F92672">│</font>    7 <font color="#66D9EF">def</font> <font color="#A6E22E">main</font>(name: <font color="#A1EFE4">str</font> = <font color="#F4BF75">&quot;morty&quot;</font>):                               <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F92672">❱ </font> 8 │   <font color="#A1EFE4">print</font>(name + <font color="#66D9EF">3</font>)                                          <font color="#F92672">│</font>
<font color="#F92672">│</font>    9                                                              <font color="#F92672">│</font>
<font color="#F92672">│</font>   10                                                              <font color="#F92672">│</font>
<font color="#F92672">│</font>   11 <font color="#66D9EF">if</font> <font color="#F92672">__name__</font> == <font color="#F4BF75">&quot;__main__&quot;</font>:                                   <font color="#F92672">│</font>
<font color="#F92672">│</font>                                                                   <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75">╭──── locals ────╮</font>                                                <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75">│</font> name = <font color="#F4BF75">&apos;morty&apos;</font> <font color="#F4BF75">│</font>                                                <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75">╰────────────────╯</font>                                                <font color="#F92672">│</font>
<font color="#F92672">╰───────────────────────────────────────────────────────────────────╯</font>
<font color="#F92672"><b>TypeError: </b></font>can only concatenate str <b>(</b>not <font color="#A6E22E">&quot;int&quot;</font><b>)</b> to str

きれいな例外の無効化

パラメータpretty_exceptions_enable=Falseを使用して、きれいな例外を完全に無効にすることもできます。

import typer

app = typer.Typer(pretty_exceptions_enable=False)


@app.command()
def main(name: str = "morty"):
    print(name + 3)


if __name__ == "__main__":
    app()

これで、他のPythonプログラムと同様に、完全な標準例外が表示されるようになります。

$ python main.py

Traceback (most recent call last):
  File "main.py", line 12, in <module>
    app()
  File "/home/user/code/superapp/env/lib/python3.7/site-packages/typer/main.py", line 328, in __call__
    raise e
  File "/home/user/code/superapp/env/lib/python3.7/site-packages/typer/main.py", line 311, in __call__
    return get_command(self)(*args, **kwargs)
  File "/home/user/code/superapp/env/lib/python3.7/site-packages/click/core.py", line 1130, in __call__
    return self.main(*args, **kwargs)
  File "/home/user/code/superapp/env/lib/python3.7/site-packages/typer/core.py", line 723, in main
    **extra,
  File "/home/user/code/superapp/env/lib/python3.7/site-packages/typer/core.py", line 216, in _main
    rv = self.invoke(ctx)
  File "/home/user/code/superapp/env/lib/python3.7/site-packages/click/core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/user/code/superapp/env/lib/python3.7/site-packages/click/core.py", line 760, in invoke
    return __callback(*args, **kwargs)
  File "/home/user/code/superapp/env/lib/python3.7/site-packages/typer/main.py", line 683, in wrapper
    return callback(**use_params)  # type: ignore
  File "main.py", line 8, in main
    print(name + 3)
TypeError: can only concatenate str (not "int") to str

環境変数_TYPER_STANDARD_TRACEBACK=1を使用しても同じ結果が得られます。

これは、他の誰かが作成したTyperプログラムで問題をデバッグする必要がある場合に備えて、他のすべてのTyperプログラムでも機能します。

export _TYPER_STANDARD_TRACEBACK=1
$ python main.py


Traceback (most recent call last):
  File "main.py", line 12, in <module>
    app()
  File "/home/user/code/superapp/env/lib/python3.7/site-packages/typer/main.py", line 328, in __call__
    raise e
  File "/home/user/code/superapp/env/lib/python3.7/site-packages/typer/main.py", line 311, in __call__
    return get_command(self)(*args, **kwargs)
  File "/home/user/code/superapp/env/lib/python3.7/site-packages/click/core.py", line 1130, in __call__
    return self.main(*args, **kwargs)
  File "/home/user/code/superapp/env/lib/python3.7/site-packages/typer/core.py", line 723, in main
    **extra,
  File "/home/user/code/superapp/env/lib/python3.7/site-packages/typer/core.py", line 216, in _main
    rv = self.invoke(ctx)
  File "/home/user/code/superapp/env/lib/python3.7/site-packages/click/core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/user/code/superapp/env/lib/python3.7/site-packages/click/core.py", line 760, in invoke
    return __callback(*args, **kwargs)
  File "/home/user/code/superapp/env/lib/python3.7/site-packages/typer/main.py", line 683, in wrapper
    return callback(**use_params)  # type: ignore
  File "main.py", line 8, in main
    print(name + 3)
TypeError: can only concatenate str (not "int") to str