Mypy 在早期返回前的实例检查后引发联合属性错误。

huangapple go评论78阅读模式
英文:

Mypy throwing union-attr errors after instance checks before early return

问题

以下是翻译好的内容:

我正试图在Python中通过将“Request”对象传递到处理程序“链”中来实现责任链模式。传递给每个处理程序方法的“Request”对象具有一些可选字段和一些联合类型。

我对这些处理程序的一般模式是在方法顶部创建一些布尔值,以检查当前处理程序方法是否应处理请求,如果不应处理,则通过调用下一个处理程序来提前返回。

以下是我的设置示例:

from pathlib import Path
from typing import Any, Callable

class Request:
    data: int | Any | None = None
    location: str | Path | None = None

def handle_int(request: Request, next_handler: Callable[[Request], int]) -> int:
    is_int = isinstance(request.data, int)
    is_str = isinstance(request.location, str)

    if not (is_int and is_str):
        return next_handler(request)

    a = request.location.split("_")

    return request.data * 3

我遇到的问题是,每当我访问请求对象的属性时,mypy会捕获“union-attr”错误:

test_mypy.py:17:9: error: Item "Path" of "str | Path | None" has no attribute "split" [union-attr]
test_mypy.py:17:9: error: Item "None" of "str | Path | None" has no attribute "split" [union-attr]
test_mypy.py:19:12: error: Unsupported operand types for * ("None" and "int") [operator]
test_mypy.py:19:12: note: Left operand is of type "int | Any | None"

我可以通过在返回语句之后添加断言来消除这些错误,但这实际上为提前返回之前的谓词检查创建了基本重复的代码,感觉不是一个好的模式。

除了创建更具体的请求类型之外,还有一种方法可以让mypy知道我们在提前返回之后意味着已经对请求中的属性形式进行了检查吗?

英文:

I am trying to implement the chain of responsibility pattern in python by passing a Request object into a handler "chain". The Request object passed in to each handler method in sequence has a few optional fields and some union types.

The general pattern I have for these handlers is to create a few boolean values at the top of the method to check if the current handler method should handle the request and if not, return early by calling the next handler in line.

The following is an example of my setup:


from pathlib import Path
from typing import Any, Callable


class Request:
    data: int | Any | None = None
    location: str | Path | None = None


def handle_int(request: Request, next_handler: Callable[[Request], int]) -> int:
    is_int = isinstance(request.data, int)
    is_str = isinstance(request.location, int)

    if not (is_int and is_str):
        return next_handler(request)

    a = request.location.split("_")

    return request.data * 3

The issue I am having is that mypy catches union-attr errors whenever I access the attributes on the request object:

test_mypy.py:17:9: error: Item "Path" of "str | Path | None" has no attribute "split"  [union-attr]
test_mypy.py:17:9: error: Item "None" of "str | Path | None" has no attribute "split"  [union-attr]
test_mypy.py:19:12: error: Unsupported operand types for * ("None" and "int")  [operator]
test_mypy.py:19:12: note: Left operand is of type "int | Any | None"

I can get rid of these by adding assertions after the return statement but that is creating basically duplicate code for the predicate checks before the early return and feels like a bad pattern.

Apart from creating more specific request types is there a way to let mypy know that the fact we are after the early return means that checks have been done on the form of the attributes n the request already?

答案1

得分: 1

mypy混淆了,因为isinstance的类型缩小不能通过变量传播。必须直接在if中完成。

使用

if not (isinstance(request.data, int) and isinstance(request.location, str)):
    return next_handler(request)

而不是

is_int = isinstance(request.data, int)
is_str = isinstance(request.location, str)

if not (is_int and is_str):
    return next_handler(request)
英文:

mypy is just getting confused because the type narrowing of isinstance cannot be propagated through variables. It has to be done directly in the if.

Use

if not (isinstance(request.data, int) and isinstance(request.location, str)):
    return next_handler(request)

Instead of

is_int = isinstance(request.data, int)
is_str = isinstance(request.location, str)

if not (is_int and is_str):
    return next_handler(request)

huangapple
  • 本文由 发表于 2023年7月13日 21:47:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/76680124.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定