跳至内容

异常和错误

当您的代码出现错误并运行时,它将显示错误和异常。

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 = '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()

现在,当您运行它时,您将看到没有局部变量的错误。

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 ↻