我有一个测试用例.py:
import pathlib
import typing as tp
# Not under my control
PathType = tp.Union[str, pathlib.Path]
def foreign(filename: PathType) -> PathType:
return filename
# Under my control
T = tp.TypeVar('T', str, pathlib.Path)
def my_func(filename: T) -> T:
return foreign(filename)
if __name__ == '__main__':
path1: str = '/abc/efg/string.py'
san_path1: str = my_func(path1)
print(san_path1, type(san_path1))
path2: pathlib.Path = pathlib.Path('/abc/efg/pathlib.py')
san_path2: pathlib.Path = my_func(path2)
print(san_path2, type(san_path2))
此文件中有两个部分。 在“不受我控制”部分中,我正在模拟不受我控制的模块的功能,但此功能的定义如此处所示。
在“在我的控制下”部分,我试图强制执行,如果我使用 str 调用 my_func 来返回 str,或者如果我使用 pathlib.Path 调用 my_func 来返回 pathlib.Path,但为了防止出现以下情况,我将使用 str 调用该函数并返回 pythlib.Path ,反之亦然。
代码运行良好。如果我运行它,输出是:
$ python testcase.py
/abc/efg/string.py <class 'str'>
/abc/efg/pathlib.py <class 'pathlib.PosixPath'>
但是 mypy 抱怨:
$ mypy testcase.py
testcase.py:17: error: Incompatible return value type (got "Union[str, Path]", expected "str")
testcase.py:17: error: Incompatible return value type (got "Union[str, Path]", expected "Path")
Found 2 errors in 1 file (checked 1 source file)
17号线是
return foreign(filename)
。
如何满足mypy?
foreign
不保证对于 str
输入会返回 str
,对于 Path
也同样返回。它仅保证类型中的内容(除非文档另有说明):无论输入如何,它将返回任一类型。由于它超出了您的控制范围,作者可以更改实现,例如它总是返回一个 Path
,从而破坏你的代码。
如果您不确定
foreign
如何工作,您可以根据需要进行转换
def my_func(filename: T) -> T:
res = foreign(filename)
return str(res) if isinstance(filename, str) else pathlib.Path(res)
或者,如果您有测试检查
foreign
返回与给定的类型相同的类型,则可以断言 my_func
中的类型
def my_func(filename: T) -> T:
res = foreign(filename)
if isinstance(filename, str):
assert instance(res, str)
return res
else:
assert instance(res, pathlib.Path)
return res
或者更好的是失败时退出
def my_func(filename: T) -> T:
res = foreign(filename)
if isinstance(filename, str) and isinstance(res, str):
return res
elif isinstance(filename, pathlib.Path) and isinstance(res, pathlib.Path):
return res
sys.exit("Fatal error") # we exit if our code's broken
或者,如果您不希望这些检查的运行时成本,并且愿意依赖运行时测试来验证
foreign
,只需 # type: ignore
return
中的 my_func
语句即可。