我正在为记录器库编写一个帮助程序,该库具有具有特定跟踪(调试)日志记录的装饰器。
代码本身是正确的(它部分基于现有库),但我很难找到如何使 mypy 接受它的类型。
问号是我在类型方面遇到问题的地方。或者也许问题更普遍
对于静态方法:
def trace_static_method(_staticmethod: staticmethod) -> staticmethod:
@wraps(_staticmethod.__func__) # this generate mypy error for incorrect type
def wrapper(*args: ???, **kwargs: ???) -> ???:
return _log_trace(_staticmethod.__func__, *args, **kwargs)
return staticmethod(wrapper) # this generate mypy error for incorrect type
对于类方法:
def trace_class_method(_classmethod: classmethod) -> classmethod:
@wraps(_classmethod.__func__) # this generate mypy error for incorrect type
def wrapper(_cls: ???, *args: ???, **kwargs: ???) -> ???:
method = _classmethod.__get__(None, _cls) # this generate mypy error for incorrect type
return _log_trace(method, *args, **kwargs)
return classmethod(wrapper) # this generate mypy error for incorrect type
日志跟踪:
def _log_trace(func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T:
name = func.__qualname__
module = func.__module__
logger_ = logger.opt(depth=1)
logger_.log("TRACE", "{}.{} CALL args={}, kwargs={}", module, name, args, kwargs)
result = func(*args, **kwargs)
logger_.log("TRACE", "{}.{} RETURN {}", module, name, result)
return result
简单函数装饰器的工作类型:
def trace(func: Callable[P, T]) -> Callable[P, T]:
@wraps(func)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
return _log_trace(func, *args, **kwargs)
return wrapper
编辑:为静态方法添加正确的类型实际上非常简单:
def trace_static_method(_staticmethod: staticmethod[P, T]) -> staticmethod[P, T]:
@wraps(_staticmethod.__func__)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
return _log_trace(_staticmethod.__func__, *args, **kwargs)
return staticmethod(wrapper)
正确的打字。适用于 mypy 1.6.1.
P = ParamSpec("P")
T = TypeVar("T")
R_co = TypeVar("R_co", covariant=True)
def _log_trace(func: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T:
"""Log a function call and return."""
trace_logger = logger.opt(depth=1).bind(
trace_name=func.__qualname__,
trace_module=func.__module__,
)
trace_logger.trace("CALL args={}, kwargs={}", args, kwargs)
result = func(*args, **kwargs)
trace_logger.trace("RETURN {}", result)
return result
def trace(func: Callable[P, T]) -> Callable[P, T]:
"""Trace a function."""
@wraps(func)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
return _log_trace(func, *args, **kwargs)
return wrapper
def trace_instance_method(func: Callable[P, T]) -> Callable[P, T]:
"""Trace a method."""
@wraps(func)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
_self: object = args[0]
method = func.__get__(_self, _self.__class__)
return _log_trace(method, *args[1:], **kwargs)
return wrapper
def trace_static_method(
_staticmethod: "staticmethod[P, R_co]",
) -> "staticmethod[P, R_co]":
"""Trace a method wrapped in @staticmethod."""
@wraps(_staticmethod.__func__)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R_co:
return _log_trace(_staticmethod.__func__, *args, **kwargs)
return staticmethod(wrapper)
def trace_class_method(
_classmethod: "classmethod[T, P, R_co]",
) -> "classmethod[T, P, R_co]":
"""Trace a method wrapped in @classmethod"""
@wraps(_classmethod.__func__)
def wrapper(_cls: type[T], *args: P.args, **kwargs: P.kwargs) -> R_co:
method = _classmethod.__get__(None, _cls)
return _log_trace(method, *args, **kwargs)
return classmethod(wrapper)