如何正确输入装饰器和装饰函数?

How to properly type decorator and decorated function?

我有一个装饰器可以捕获请求中的错误:

def catch_request_errors(func):
    @functools.wraps(func)
    def inner_function(*args, **kwargs):  # -> Optional[Dict]
        try:
            response = func(*args, **kwargs)
        except Exception as e:
            capture_message(str(e), level='error')
            errors = {'error': str(e)}
            return errors

        if not response.ok:
            capture_message(response.text, level='error')
            errors = {'error': str(response.text)}
            return errors

        return None

    return inner_function

装饰器本身 returns Optional[Dict] - 发生错误时应该 return {'error': 'error_msg_here' } 或 None 如果没有错误。

这是我的装饰函数,实际上 returns Response 没有装饰器,但是有装饰器 returns Optional[字典]:

@catch_request_errors
def make_request(self, url: str, data: Dict): # -> Optional[Dict]
    return requests.post(url, data)  # but there is actually -> Response

我不得不像那样“硬打字”来欺骗我的 IDE,但我应该如何正确地打字?

request_errors = self.make_request(settings.DOCUMENT_GENERATOR_URL, data)  # type: Optional[Dict]

有点类似于subprocess.Popen.comunicate

中的解决方案
def catch_request_errors(func):
    @functools.wraps(func)
    def inner_function(*args, **kwargs):
        response = None
        errors = None
        try:
            response = func(*args, **kwargs)
        except Exception as e:
            capture_message(str(e), level='error')
            errors = {'error': str(e)}

        if response is not None and not response.ok:
            capture_message(response.text, level='error')
            errors = {'error': str(response.text)}

        return response, errors

    return inner_function
@catch_request_errors
def make_request(self, url, data):
    return requests.post(url, data)
response, errors = make_request(URL, DATA)
if errors is not None:
    # handle errors
else:
    # code ...

如果有人遇到类似问题,我会发布我的解决方案,感谢@woblob

装饰者:

def catch_request_errors(func: Callable[[str, Dict], Response]) -> Callable[[Tuple[Any], Dict[str, Any]],
                                                                            Tuple[Response, Optional[Dict]]]:
    @functools.wraps(func)
    def inner_function(*args, **kwargs) -> Tuple[Response, Optional[Dict]]:
        response = None
        errors = None
        try:
            response = func(*args, **kwargs)
        except Exception as e:
            capture_message(str(e), level='error')
            errors = {'error': str(e)}

        if response and not response.ok:
            capture_message(response.text, level='error')
            errors = {'error': str(response.text)}

        return response, errors

    return inner_function
@catch_request_errors
def make_request(self, url: str, data: Dict) -> Response:
    return requests.post(url, data)
response, errors = self.make_request(settings.DOCUMENT_GENERATOR_URL, data)