-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
aiodec.py
90 lines (72 loc) · 2.43 KB
/
aiodec.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
"""
aiodec
======
Decorators for coroutines
"""
import time
import logging
from functools import wraps
from string import Template
import inspect
from inspect import Signature
from typing import Callable, Optional, Mapping, Any
__version__ = '2018.10.1'
logger = logging.getLogger(__name__)
Callback = Callable[[Signature, Mapping[str, Any]], None]
def adecorator(
f=None,
pre_callback: Optional[Callback] = None,
post_callback: Optional[Callback]= None):
def inner(g):
@wraps(g)
async def wrapper(*args, **kwargs):
# Get the function signature of the wrapped function. We need this
# in order to obtain all the parameter information.
template_parameters = dict(name_=g.__name__, qualname_=g.__qualname__)
sig = inspect.signature(g)
# Using the actually-provided args, bind them to the signature
# of the wrapped function
bound_args = sig.bind(*args, **kwargs)
# Now fill in the unsupplied parameters with their default
# values.
bound_args.apply_defaults()
template_parameters.update(bound_args.arguments)
pre_callback and pre_callback(sig, template_parameters)
try:
return await g(*args, **kwargs)
finally:
post_callback and post_callback(sig, template_parameters)
return wrapper
if f:
# astopwatch() was called WITHOUT evaluation, so we must return the
# actual wrapper function that replaces f.
return inner(f)
else:
# astopwatch() was called WITH evaluation, so we need to return ANOTHER
# decorator (that will receive and wrap function f).
return inner
def astopwatch(
f=None,
message_template='Time taken: $time_ seconds',
fmt='%.4g',
logger=logger,
):
# Using templates because safe_substitute is awesome.
tmpl = Template(message_template)
t0 = 0
def pre_callback(sig, template_parameters):
nonlocal t0
t0 = time.perf_counter()
def post_callback(sig, template_parameters):
nonlocal t0
dt = time.perf_counter() - t0
msg = tmpl.safe_substitute(
**template_parameters,
time_=fmt % dt
)
logger.info(msg)
return adecorator(
f,
pre_callback=pre_callback,
post_callback=post_callback
)