我可以在影响原始参数的函数内部执行 Mypy 断言吗?

问题描述 投票:0回答:1

假设我有一个简单的验证函数:

def is_valid_build_target(target: Any, throw=False) -> bool:
    target = str(target)
    allowed_targets = ["dev", "prod"]
    is_allowed = target.lower() in ALLOWED_TARGETS
    if not is_allowed and throw:
        raise ValueError(
            f"Invalid target '{target}'. Must be one of: {ALLOWED_TARGETS}"
        )

    assert target is not None
    return is_allowed

但是如果我想使用这个函数,Mypy 不会将该断言传递回堆栈(大概是因为

target
是一个原语,所以它不是通过引用传递的):

Target = Literal["dev", "prod"]
target: Target | None = cast(Target | None, os.getenv("APP_TARGET", None))

if not is_valid_build_target(target):
    raise ValueError(f"Invalid target, I could have used throw=True, but I wanted a custom error message")

# Mypy still thinks `target` could be None

如果我内联执行函数逻辑,或者之后使用

assert
,那么它就可以工作,但现在我没有将运行时验证逻辑保留在独立函数中:

if not is_valid_build_target(target):
    raise ValueError(f"Invalid target...")
assert target is not None

# happy Mypy

验证函数内部有什么方法可以验证这一点并让 Mypy 满意吗?

python python-3.x mypy python-typing
1个回答
0
投票

我会让你的验证函数执行以下两件事之一:

  1. 返回有效值,或者
  2. 提出例外

我还会首先检查

os.getenv
返回
None
,因为根本不存在的变量与无效字符串值是一个不同的问题(尽管具有相同的可能解决方案)。

from typing import Literal, cast


Target = Literal["dev", "prod"]
ALLOWED_TARGETS = typing.get_args(Target)

def valid_build_target(target: str) -> Target:
    lower_target = target.lower()
    if lower_target in ALLOWED_TARGETS:
        # It's a shame type narrowing doesn't work here.
        return cast(Target, lower_target)
    else:
        raise ValueError(
            f"Invalid target '{target}'. Must be one of: {ALLOWED_TARGETS}"
        )

possible_target = os.getenv("APP_TARGET")
if possible_target is None:
    raise ValueError("APP_TARGET not defined")

target: Target = valid_build_target(possible_target)
   
© www.soinside.com 2019 - 2024. All rights reserved.