Source code for skactiveml.utils._functions

import inspect
from types import MethodType
from makefun import with_signature


[docs]def call_func( f_callable, only_mandatory=False, ignore_var_keyword=False, **kwargs ): """Calls a function with the given parameters given in `kwargs`, if they exist as parameters in `f_callable`. Parameters ---------- f_callable : callable The function or object that is to be called. only_mandatory : boolean, default=False If `True`, only mandatory parameters are set. ignore_var_keyword : boolean, default=False If `False`, all kwargs are passed when `f_callable` uses a parameter that is of kind `Parameter.VAR_KEYWORD`, i.e., `**kwargs`. For further reference see the `inspect` package. kwargs : kwargs All parameters that could be used for calling f_callable. Returns ------- f_callable_result : return type of `f_callable` The return value of f_callable. """ params = inspect.signature(f_callable).parameters param_keys = params.keys() if only_mandatory: param_keys = list( filter(lambda k: params[k].default == params[k].empty, param_keys) ) has_var_keyword = any( filter(lambda p: p.kind == p.VAR_KEYWORD, params.values()) ) if has_var_keyword and not ignore_var_keyword and not only_mandatory: vars = kwargs else: vars = dict(filter(lambda e: e[0] in param_keys, kwargs.items())) return f_callable(**vars)
[docs]def match_signature(wrapped_obj_name, func_name): """A decorator that matches the signature to a given method from a reference and hides it when the reference object does not have the wrapped function. This is especially helpful for wrapper classes whose functions should appear. This decorator is heavily inspired by the `available_if` decorator from `sklearn.utils.metaestimators`. Parameters ---------- wrapped_obj_name : str The name of the object that will be wrapped. func_name : str The name of the function that will be wrapped. Returns ------- wrapped_obj : callable The wrapped function. """ class _MatchSignatureDescriptor: """_MatchSignatureDescriptor A descriptor that allows a wrapper to clone the signature of a method `func_name` from the wrapped object `wrapped_obj_name`. Furthermore, this extends upon the conditional property as implemented in `available_if` from `sklearn.utils.metaestimators`. Parameters ---------- fn: MethodType The method that should be wrapped. wrapped_obj_name: str The name of the wrapped object within the wrapper class. func_name : str The method name of the function that should be wrapped. """ def __init__(self, fn, wrapped_obj_name, func_name): self.fn = fn self.wrapped_obj_name = wrapped_obj_name self.func_name = func_name self.__name__ = func_name def __get__(self, obj, owner=None): """Wrap the method specified in `self.func_name` from the wrapped object `self.wrapped_obj_name` such that the signature will be the same. Parameters ---------- obj : object The wrapper object. This parameter will be None, if the method is accessed via the class and not an instantiated object. owner : class, default=None The wrapper class. Returns ------- The wrapped method. """ if obj is not None: reference_object = getattr(obj, self.wrapped_obj_name) if not hasattr(reference_object, self.func_name): raise AttributeError( f"This {reference_object} has" f" no method {self.func_name}." ) reference_function = getattr(reference_object, self.func_name) # Check if the refrenced function is a method of that object. # If it is, use `__func__` to copy the name of the `self` # parameter # If it is not, (i.e. it has been added as a lambda function), # add a provisory self argument in the first position if hasattr(reference_function, "__func__"): new_sig = inspect.signature(reference_function.__func__) else: reference_sig = inspect.signature(reference_function) new_parameters = list(reference_sig.parameters.values()) new_parameters.insert( 0, inspect.Parameter( "self", kind=inspect._ParameterKind.POSITIONAL_OR_KEYWORD, ), ) new_sig = inspect.Signature( new_parameters, return_annotation=reference_sig.return_annotation, ) # create a wrapper with the new signature and the correct # function name function_decorator = with_signature( func_signature=new_sig, func_name=self.fn.__name__, ) fn = function_decorator(self.fn) out = MethodType(fn, obj) else: out = self.fn return out return lambda fn: _MatchSignatureDescriptor( fn, wrapped_obj_name, func_name=func_name )