Source code for RiskQuantLib.Tool.decoratorTool

#!/usr/bin/python
#coding = utf-8

import time
import inspect
import numpy as np
import pandas as pd
from functools import wraps
from typing import Optional, Sequence, Callable, Any, List, TypeVar

#<import>
#</import>

F = TypeVar('F', bound=Callable[..., Any])

[docs]class timer(object): """ This is a decorator which will count the time of a single function call. If this decorator is passed a prefix, it will print it along with running time of decorated function. """ def __init__(self, prefix: str='None'): self.prefix = prefix def __call__(self, func: F) -> F: @wraps(func) def wrapper(*args, **kwargs) -> Any: start = time.time() result = func(*args, **kwargs) print("Function Hint: {} ; Run Time: {} sec ; Finished At: {}".format(self.prefix, round(time.time() - start,2), pd.Timestamp.today())) return result return wrapper
#<timer> #</timer>
[docs]class confirmer(object): """ This is a decorator which will require confirmation before calling the wrapped function. If this decorator is passed a notice, it will print it before require confirmation. """ def __init__(self, prefix: str='This Is An Action That Can NOT Be Cancelled, Input Y/y To Confirm Or N/n To Deny:'): self.prefix = prefix def __call__(self, func: F) -> F: @wraps(func) def wrapper(*args, **kwargs) -> Any: confirm = input(self.prefix) if confirm.lower() == 'y': print('Action Confirmed, Processing On-going') result = func(*args, **kwargs) return result else: print('Action Denied, Nothing Changed') return None return wrapper
#<confirmer> #</confirmer>
[docs]class checker(object): """ This is a decorator which will check every parameter what is passed into a function. By default, this decorator does nothing. """ def __init__(self, argIndexList: Optional[List[int]] = None, argKeyList: Optional[List[str]] = None, argIterableIndexList: Optional[List[int]] = None, argIterableKeyList: Optional[List[str]] = None): self.argIndexSet = set(argIndexList) if argIndexList else {} self.argKeySet = set(argKeyList) if argKeyList else {} self.argIterableIndexSet = set(argIterableIndexList) if argIterableIndexList else {} self.argIterableKeySet = set(argIterableKeyList) if argIterableKeyList else {} def __call__(self, func: F) -> F: @wraps(func) def wrapper(*args, **kwargs) -> Any: return func(*args, **kwargs) return wrapper
#<checker> #</checker>
[docs]class stringTimestampConverter(checker): """ This is a decorator which will try to convert string into pandas.Timestamp. It is used when a function require a pandas.Timestamp parameter, after decorated by this, it allows to pass a string into parameter. If a function is typed as func(x: pandas.Timestamp, y: pandas.Timestamp, z: List[pandas.Timestamp], m: List[pandas.Timestamp]), then you can decorate it by @stringTimestampConverter(argIndexList=[0,1], argKeyList=['x','y'], argIterableIndexList=[2,3], argIterableKeyList=['z','m']) """
[docs] @staticmethod def convertStrToTimestamp(arg: Optional[Any] = None): return pd.Timestamp(arg) if isinstance(arg, str) else arg
[docs] @staticmethod def convertStrListToTimestampList(arg: Optional[Sequence] = None): return [stringTimestampConverter.convertStrToTimestamp(a) for a in arg] if arg else arg
def __call__(self, func: F) -> F: @wraps(func) def wrapper(*args, **kwargs) -> Any: argsNew = [stringTimestampConverter.convertStrToTimestamp(arg) if idx in self.argIndexSet else stringTimestampConverter.convertStrListToTimestampList(arg) if idx in self.argIterableIndexSet else arg for idx,arg in enumerate(args)] kwargsNew = {k: (stringTimestampConverter.convertStrToTimestamp(kwargs[k]) if k in self.argKeySet else stringTimestampConverter.convertStrListToTimestampList(kwargs[k]) if k in self.argIterableKeySet else kwargs[k]) for k in kwargs} return func(*argsNew, **kwargsNew) return wrapper
#<stringTimestampConverter> #</stringTimestampConverter>
[docs]class valueAsserter(checker): """ This is a decorator which will assert that some parameter must have certain values. If passed with other values, exception will be raised. If a parameter is an iterable, then check every element. If you want to require the value of the second parameter of a function into 1 or 2, all other values should be invalid, then decorate it by @valueAsserter(argIndexList=[1],value={1,2}). """
[docs] @staticmethod def assertValue(arg: Optional[Any] = None, value: Optional[set] = None, argName: Optional[str] = None, funcName: Optional[str] = None): validated = (arg in value) if value else True if not validated: raise ValueError(f"Parameter '{argName}' in function '{funcName}' has an invalid value: '{arg}'. Allowed values are: {value}")
[docs] @staticmethod def assertValueList(arg: Optional[Sequence] = None, value: Optional[set] = None, argName: Optional[str] = None, funcName: Optional[str] = None): validated = np.all([i in value for i in arg]) if arg and value else True if not validated: raise ValueError(f"Parameter '{argName}' in function '{funcName}' has at least an invalid value in its element: '{arg}'. Allowed values of element are: {value}")
def __init__(self, argIndexList: Optional[List[int]] = None, argKeyList: Optional[List[str]] = None, argIterableIndexList: Optional[List[int]] = None, argIterableKeyList: Optional[List[str]] = None, value: Optional[set] = None): super(valueAsserter, self).__init__(argIndexList=argIndexList, argKeyList=argKeyList, argIterableIndexList=argIterableIndexList, argIterableKeyList=argIterableKeyList) self.value = value def __call__(self, func: F) -> F: @wraps(func) def wrapper(*args, **kwargs) -> Any: funcName = func.__name__ argNameList = list(inspect.signature(func).parameters.keys()) [valueAsserter.assertValue(arg, self.value, argNameList[idx], funcName) if idx in self.argIndexSet else valueAsserter.assertValueList(arg, self.value, argNameList[idx], funcName) if idx in self.argIterableIndexSet else None for idx,arg in enumerate(args)] [(valueAsserter.assertValue(kwargs[k], self.value, k, funcName) if k in self.argKeySet else valueAsserter.assertValueList(kwargs[k], self.value, k, funcName) if k in self.argIterableKeySet else None) for k in kwargs] return func(*args, **kwargs) return wrapper
#<valueAsserter> #</valueAsserter>
[docs]class freezer(object): """ This is a decorator which will freeze all parameters except the first one. If you have a function typed as func(x, y, z, m), you can convert it into a single-parameter function by passing y, z, m into it before x is determined. You can do this by @freezer(y=1, z=2, m=3), then just call func(43), it will be totally the same as func(x=43, y=1, z=2, m=3) """ def __init__(self, *args, **kwargs) -> None: self.args = args self.kwargs = kwargs def __call__(self, func: F) -> F: @wraps(func) def wrapper(argFirst) -> Any: return func(argFirst, *self.args, **self.kwargs) return wrapper
#<freezer> #</freezer> #<decoratorTool> #</decoratorTool>