Source code for mmodel.modifier
from functools import wraps
from inspect import signature, Parameter, Signature
[docs]def loop_input(parameter: str):
"""Modify function to iterate one given parameter.
:param list parameter: target parameter to loop
The target parameter name is changed to f"{param}_loop"
"""
def loop(func):
param_list = []
for param in signature(func).parameters.values():
if param.name == parameter:
param_list.append(Parameter(f"{param}_loop", kind=1))
else:
param_list.append(param)
new_sig = Signature(param_list)
@wraps(func)
def loop_wrapped(**kwargs):
"""Isolate the loop parameter and loop over the values."""
loop_values = kwargs.pop(f"{parameter}_loop")
return [func(**kwargs, **{parameter: value}) for value in loop_values]
loop_wrapped.__signature__ = new_sig
return loop_wrapped
loop.metadata = f"loop_input({repr(parameter)})"
return loop
[docs]def zip_loop_inputs(parameters: list):
"""Modify function to iterate the parameters pairwise.
:param list parameters: list of the parameters to loop
only one parameter is allowed. If a string of the parameters is
provided, the parameters should be delimited by ", ".
"""
def zip_loop(func):
@wraps(func)
def loop_wrapped(**kwargs):
loop_values = [kwargs.pop(param) for param in parameters]
result = []
for value in zip(*loop_values): # unzip the values
loop_value_dict = dict(zip(parameters, value))
rv = func(**kwargs, **loop_value_dict)
result.append(rv)
return result
return loop_wrapped
zip_loop.metadata = f"zip_loop({repr(parameters)})"
return zip_loop
[docs]def format_time(dt, precision):
"""Format time in seconds to a human-readable string."""
units = {"s": 1.0, "ms": 1e-3, "us": 1e-6, "ns": 1e-9}
for unit, scale in units.items():
if dt >= scale:
return f"{dt / scale:.{precision}f} {unit}"
[docs]def profile_time(number=1, repeat=1, verbose=False, precision=2):
"""Profile the execution time of a function.
The modifier behaves similarly to the *timeit* module. However,
the modifier does not suppress garbage collection during the function
execution; therefore, the result might be slightly different.
"""
import timeit
timer = timeit.default_timer
def timeit_modifier(func):
@wraps(func)
def wrapped(**kwargs):
time_list = []
for _ in range(repeat):
t0 = timer()
for _ in range(number):
result = func(**kwargs)
t1 = timer()
time_list.append((t1 - t0) / number)
if verbose:
print(f"{func.__name__} - raw times: {time_list}")
else:
term = "loops" if number > 1 else "loop"
min_time = format_time(min(time_list), precision)
print(
f"{func.__name__} - {number} {term}, "
f"best of {repeat}: {min_time} per loop"
)
return result
return wrapped
timeit_modifier.metadata = (
f"profile_time(number={number}, repeat={repeat}, verbose={verbose})"
)
return timeit_modifier