例外とエラー¶
コードにエラーがあり、実行すると、エラーと例外が表示されます。
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">"morty"</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">"__main__"</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">'morty'</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">"int"</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">"__main__"</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">"int"</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"><module></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">"__main__"</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><</b><font color="#AE81FF"><b>module</b></font> <font color="#F4BF75">'builtins'</font> <b>(</b>built-in<b>)></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">'main.py'</font> <font color="#F4BF75">│</font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75">│</font> <font color="#A6194C">__loader__</font> = <b><</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>></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">'__main__'</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><</b><font color="#AE81FF"><b>typer.main.Typer</b></font> object at <font color="#66D9EF">0x7f047db51d90</font><b>></b> <font color="#F4BF75">│</font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75">│</font> main = <b><</b><font color="#AE81FF"><b>function</b></font> main at <font color="#66D9EF">0x7f047db56830</font><b>></b> <font color="#F4BF75">│</font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75">│</font> typer = <b><</b><font color="#AE81FF"><b>module</b></font> <font color="#F4BF75">'typer'</font> from <font color="#F4BF75">│</font> <font color="#F92672">│</font>
<font color="#F92672">│</font> <font color="#F4BF75">│</font> <font color="#F4BF75">'/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">"morty"</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">"__main__"</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">'morty'</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">"int"</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