from functools import wraps
from typing import Any, Callable
def timer(func: Callable[..., Any]) -> Callable[..., Any]:
"""Calculates the runtime of a function, and outputs it to logging.DEBUG."""
@wraps(func)
def wrapper(*args, **kwargs):
start = perf_counter()
value = func(*args, **kwargs)
end = perf_counter()
_logger = logging.getLogger(__name__ + "." + func.__name__)
_logger.debug(" runtime: {:.4f} seconds".format(end - start))
return value
return wrapper
The problem with this approach is that now, MyPy loses the return type, or rather, it degenerates into Any, so consider:
import logging
from typing import Callable, Any
from time import perf_counter
from functools import wraps
def timer(func: Callable[..., Any]) -> Callable[..., Any]:
"""Calculates the runtime of a function, and outputs it to logging.DEBUG."""
@wraps(func)
def wrapper(*args, **kwargs):
start = perf_counter()
value = func(*args, **kwargs)
end = perf_counter()
_logger = logging.getLogger(__name__ + '.' + func.__name__)
_logger.debug(' runtime: {:.4f} seconds'.format(end - start))
return value
return wrapper
@timer
def func(x:int) -> int:
return x
def string_func(s: str):
return s[:]
x = 42 * func(42)
reveal_type(x)
string_func(x)
Using:
(py37) Juans-MacBook-Pro:tempdata juan$ mypy --version
mypy 0.641
If I try to type-check this, it passes!
(py37) Juans-MacBook-Pro:tempdata juan$ mypy typing_decorators.py
typing_decorators.py:29: error: Revealed type is 'Any'
I've found one solution in the case where you want to preserve exactly the arguments, which is to use a TypeVar and to cast the wrapper, that way MyPy can know exactly the type (assuming the original function is annoated):
import logging
from typing import Callable, Any, TypeVar, cast
from time import perf_counter
from functools import wraps
F = TypeVar('F', bound=Callable[..., Any])
def timer(func: F) -> F:
"""Calculates the runtime of a function, and outputs it to logging.DEBUG."""
@wraps(func)
def wrapper(*args, **kwargs):
start = perf_counter()
value = func(*args, **kwargs)
end = perf_counter()
_logger = logging.getLogger(__name__ + '.' + func.__name__)
_logger.debug(' runtime: {:.4f} seconds'.format(end - start))
return value
return cast(F, wrapper)
@timer
def func(x:int) -> int:
return x
def string_func(s: str):
return s[:]
x = 42 * func(42)
reveal_type(x)
string_func(x)
In this case:
(py37) Juans-MacBook-Pro:tempdata juan$ mypy typing_decorators.py
typing_decorators.py:32: error: Revealed type is 'builtins.int'
typing_decorators.py:34: error: Argument 1 to "string_func" has incompatible type "int"; expected "str"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With