异常和错误
当您的代码出现错误并运行时,它将显示错误和异常。
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 内部部分的所有内容。
因此,您看到的错误将 **更加清晰** 和简单,帮助您快速检测代码中的问题。
fast →python main.py
╭──────────────── Traceback (most recent call last) ────────────────╮
│ /home/user/code/superapp/main.py:5 in main │
│ │
│ 2 │
│ 3 │
│ 4 def main(name: str = "morty"): │
│ ❱ 5 │ print(name + 3) │
│ 6 │
│ 7 │
│ 8 if __name__ == "__main__": │
│ │
│ ╭──── locals ────╮ │
│ │ name = 'morty' │ │
│ ╰────────────────╯ │
╰───────────────────────────────────────────────────────────────────╯
TypeError: can only concatenate str (not "int") to str
restart ↻
没有 Rich 的异常
如果您没有安装 Rich,Typer 仍然会使用一些技巧来尽可能 **清晰地** 向您显示信息。
fast →python main.py
Traceback (most recent call last):
File "main.py", line 12, in
typer.run(main)
File "main.py", line 8, in main
print(name + 3)
TypeError: can only concatenate str (not "int") to str
restart ↻
出于安全原因禁用局部变量
如果您的 Typer 应用程序处理 **敏感信息**,例如 **密码**、**密钥**、**令牌**,那么如果自动错误显示这些 局部变量 中的值,可能会出现问题。
这在您的 CLI 应用程序在记录日志的某些 CI(持续集成)系统上运行时尤其重要。
使用 Rich 时,上面的默认错误会显示一个包含以下内容的部分:
在这种情况下,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()
现在,当您运行它时,您将看到没有局部变量的错误。
fast →python main.py supersecret
╭──────────────── Traceback (most recent call last) ────────────────╮
│ /home/user/code/superapp/main.py:8 in main │
│ │
│ 5 │
│ 6 @app.command() │
│ 7 def main(password: str): │
│ ❱ 8 │ print(password + 3) │
│ 9 │
│ 10 │
│ 11 if __name__ == "__main__": │
╰───────────────────────────────────────────────────────────────────╯
TypeError: can only concatenate str (not "int") to str
restart ↻
请注意,您传递了密码 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()
现在,当您运行它时,您将看到完整的输出。
fast →python main.py
╭──────────────── Traceback (most recent call last) ────────────────╮
│ /home/user/code/superapp/main.py:12 in <module> │
│ │
│ 9 │
│ 10 │
│ 11 if __name__ == "__main__": │
│ ❱ 12 │ app() │
│ 13 │
│ │
│ ╭─────────────────────────── locals ────────────────────────────╮ │
│ │ __annotations__ = {} │ │
│ │ __builtins__ = <module 'builtins' (built-in)> │ │
│ │ __cached__ = None │ │
│ │ __doc__ = None │ │
│ │ __file__ = 'main.py' │ │
│ │ __loader__ = <_frozen_importlib_external.SourceFileLoad… │ │
│ │ object at 0x7f047db1c050> │ │
│ │ __name__ = '__main__' │ │
│ │ __package__ = None │ │
│ │ __spec__ = None │ │
│ │ app = <typer.main.Typer object at 0x7f047db51d90> │ │
│ │ main = <function main at 0x7f047db56830> │ │
│ │ typer = <module 'typer' from │ │
│ │ '/home/user/code/superapp/env/lib/python3.… │ │
│ ╰───────────────────────────────────────────────────────────────╯ │
│ │
│ /home/user/code/superapp/env/lib/python3.7/site-packages/typer/ma │
│ in.py:328 in __call__ │
│ │
│ /home/user/code/superapp/env/lib/python3.7/site-packages/typer/ma │
│ in.py:311 in __call__ │
│ │
│ /home/user/code/superapp/env/lib/python3.7/site-packages/click/co │
│ re.py:1130 in __call__ │
│ │
│ /home/user/code/superapp/env/lib/python3.7/site-packages/typer/co │
│ re.py:723 in main │
│ │
│ /home/user/code/superapp/env/lib/python3.7/site-packages/typer/co │
│ re.py:216 in _main │
│ │
│ /home/user/code/superapp/env/lib/python3.7/site-packages/click/co │
│ re.py:1404 in invoke │
│ │
│ /home/user/code/superapp/env/lib/python3.7/site-packages/click/co │
│ re.py:760 in invoke │
│ │
│ /home/user/code/superapp/env/lib/python3.7/site-packages/typer/ma │
│ in.py:683 in wrapper │
│ │
│ /home/user/code/superapp/main.py:8 in main │
│ │
│ 5 │
│ 6 @app.command() │
│ 7 def main(name: str = "morty"): │
│ ❱ 8 │ print(name + 3) │
│ 9 │
│ 10 │
│ 11 if __name__ == "__main__": │
│ │
│ ╭──── locals ────╮ │
│ │ name = 'morty' │ │
│ ╰────────────────╯ │
╰───────────────────────────────────────────────────────────────────╯
TypeError: can only concatenate str (not "int") to str
restart ↻
禁用漂亮异常
您还可以使用参数 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 程序相同的完整标准异常。
fast →python main.py
Traceback (most recent call last):
File "main.py", line 12, in
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
restart ↻
您也可以使用环境变量 _TYPER_STANDARD_TRACEBACK=1
达到相同的效果。
这对于任何其他 Typer 程序也适用,如果您需要调试其他人编写的 Typer 程序中的问题。
fast →export _TYPER_STANDARD_TRACEBACK=1python main.py
Traceback (most recent call last):
File "main.py", line 12, in
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
restart ↻