使用 click 的 pass_context 时如何修复 mypy 错误
How to fix mypy error when using click's pass_context
我正在使用 click to build a command line application. I am using mypy 进行类型检查。
但是,使用 @pass_context
将上下文传递给函数会按预期工作,但 mypy 失败并显示错误:
error: Argument 1 to "print_or_exit" has incompatible type "str"; expected "Context"
我不明白为什么。以下是重现此 mypy 错误的 MWE:
import click
from typing import Optional
@click.pass_context
def print_or_exit(ctx: click.Context, some_txt: Optional[str] = "") -> None:
if ctx.params.get("exit_", False):
exit(1)
print(some_txt)
@click.command(context_settings=dict(help_option_names=["-h", "--help"]))
@click.option("--exit","-e", "exit_", is_flag=True, help="exit")
@click.pass_context
def main(ctx: click.Context, exit_: bool) -> None:
print_or_exit("bla")
if __name__ == "__main__":
main()
运行脚本使用参数-e
,则脚本存在,无需打印到终端;当省略 -e
时,脚本会打印到终端,因此一切都按预期进行。
那么,为什么 mypy 会失败?
我查看了 sources of click、decorators.py
F = t.TypeVar("F", bound=t.Callable[..., t.Any])
FC = t.TypeVar("FC", t.Callable[..., t.Any], Command)
def pass_context(f: F) -> F:
"""Marks a callback as wanting to receive the current context
object as first argument.
"""
def new_func(*args, **kwargs): # type: ignore
return f(get_current_context(), *args, **kwargs)
return update_wrapper(t.cast(F, new_func), f)
因此,函数 pass_context
return 接收参数 (f: F
) 的相同类型 (-> F
)。因此,mypy 期望您在 print_or_exit
.
中传递两个参数
我认为最好的解决方案是尽可能明确地通过 ctx
。这样做的好处——您可以在 print_or_exit
函数的测试中轻松模拟 ctx
。所以,我建议这个代码:
import click
from typing import Optional
def print_or_exit(ctx: click.Context, some_txt: Optional[str] = "") -> None:
if ctx.params.get("exit_", False):
exit(1)
print(some_txt)
@click.command(context_settings=dict(help_option_names=["-h", "--help"]))
@click.option("--exit","-e", "exit_", is_flag=True, help="exit")
@click.pass_context
def main(ctx: click.Context, exit_: bool) -> None:
print_or_exit(ctx, "bla")
if __name__ == "__main__":
main()
它按预期工作并通过了 mypy
我正在使用 click to build a command line application. I am using mypy 进行类型检查。
但是,使用 @pass_context
将上下文传递给函数会按预期工作,但 mypy 失败并显示错误:
error: Argument 1 to "print_or_exit" has incompatible type "str"; expected "Context"
我不明白为什么。以下是重现此 mypy 错误的 MWE:
import click
from typing import Optional
@click.pass_context
def print_or_exit(ctx: click.Context, some_txt: Optional[str] = "") -> None:
if ctx.params.get("exit_", False):
exit(1)
print(some_txt)
@click.command(context_settings=dict(help_option_names=["-h", "--help"]))
@click.option("--exit","-e", "exit_", is_flag=True, help="exit")
@click.pass_context
def main(ctx: click.Context, exit_: bool) -> None:
print_or_exit("bla")
if __name__ == "__main__":
main()
运行脚本使用参数-e
,则脚本存在,无需打印到终端;当省略 -e
时,脚本会打印到终端,因此一切都按预期进行。
那么,为什么 mypy 会失败?
我查看了 sources of click、decorators.py
F = t.TypeVar("F", bound=t.Callable[..., t.Any])
FC = t.TypeVar("FC", t.Callable[..., t.Any], Command)
def pass_context(f: F) -> F:
"""Marks a callback as wanting to receive the current context
object as first argument.
"""
def new_func(*args, **kwargs): # type: ignore
return f(get_current_context(), *args, **kwargs)
return update_wrapper(t.cast(F, new_func), f)
因此,函数 pass_context
return 接收参数 (f: F
) 的相同类型 (-> F
)。因此,mypy 期望您在 print_or_exit
.
我认为最好的解决方案是尽可能明确地通过 ctx
。这样做的好处——您可以在 print_or_exit
函数的测试中轻松模拟 ctx
。所以,我建议这个代码:
import click
from typing import Optional
def print_or_exit(ctx: click.Context, some_txt: Optional[str] = "") -> None:
if ctx.params.get("exit_", False):
exit(1)
print(some_txt)
@click.command(context_settings=dict(help_option_names=["-h", "--help"]))
@click.option("--exit","-e", "exit_", is_flag=True, help="exit")
@click.pass_context
def main(ctx: click.Context, exit_: bool) -> None:
print_or_exit(ctx, "bla")
if __name__ == "__main__":
main()
它按预期工作并通过了 mypy