def __init__(self, module: BaseEstimator): """ Parameters ---------- module : :class:`sklearn.base.BaseEstimator` the module to wrap """ super().__init__() self.module = module # forwards methods to self.module if necessary for key in ["fit", "partial_fit", "predict"]: if hasattr(self.module, key): setattr(self, key, getattr(self.module, key)) # if estimator is build dynamically based on input, classes have to # be passed at least at first time (we pass it every time), because # not every class is present in every batch # variable is initialized here, but feeded during the training if (self.iterative_training and "classes" in get_signature(self.partial_fit).parameters): self.classes = None
def patch_endpoint_signature( endpoint: Callable, handler: Callable = None, dependencies: Iterable[Tuple[str, Type]] = None, ) -> Callable: signature = get_signature(endpoint) parameters = chain( _build_dependencies_parameters(dependencies or ()), _get_signature_parameters(signature), _get_signature_parameters(get_signature(handler)) if handler else [], ) # Parameters defined in handler function can overlap predefined ones from dependencies, # so we need to filter it out, as dependency/endpoint params have greater priority unique_parameters: List[Parameter] = [] pos_param_index = 0 for param in distinct(parameters, attrgetter('name')): if param.kind in _POS_PARAM_KINDS: unique_parameters.insert(pos_param_index, param) pos_param_index += 1 else: unique_parameters.append(param) endpoint.__signature__ = signature.replace(parameters=unique_parameters) # type: ignore return endpoint
def xf_api(f, name=None): """decorator to apply to the entry points of the transforms module""" api_call = name if name is not None else f.__name__ if not api_call in API: raise RuntimeError("'%s' is not part of the transforms API.") try: fn_def = globals()['DEF_' + api_call] except KeyError: # This happens if there is no definition for the decorated function raise RuntimeError("'%s' definition not found." % api_call) try: # python 2 _string_type = basestring except NameError: # This will happen on python 3 _string_type = str try: if not (isinstance(fn_def.__doc__, _string_type) and callable(fn_def._PRECOND) and callable(fn_def._POSTCOND) and callable(fn_def._signature)): raise Exception() except Exception: # A valid definition requires a string doc, and callable _PRECOND, # _POSTCOND and _signature. # # __doc__ will become the decorated function's documentation. # _PRECOND will be run on every call with args and kwargs # _POSTCOND will be run on every call with result, args and kwargs # _signature will be used to enforce a signature on implementations. # # _PRECOND and _POSTCOND will only be called if CHECK_API is enabled, # as they will slow down execution. raise RuntimeError("'{0}' definition error.".format(api_call)) # Sanity check: make sure the decorated function has the expected signature. if get_signature is not None: # Check that the function has the right signature if get_signature(fn_def._signature) != get_signature(f): raise RuntimeError("'{0}' signature mismatch.".format(api_call)) # At this point use a wrapper that calls pre and post conditions if checking # is enabled, otherwise leave the function "as is". if CHECK_API: @functools.wraps(f, assigned={"__doc__": fn_def.__doc__}) def wrapper(*args, **kwargs): fn_def._PRECOND(*args, **kwargs) result = f(*args, **kwargs) fn_def._POSTCOND(result, *args, **kwargs) return result return wrapper else: # just try to put the right documentation on the function try: f.__doc__ = fn_def.__doc__ except Exception: pass return f
def _syntax_rule(f, transformer, debug): mangle = str(time()).replace(".", "_") assert isinstance(f, FunctionType) sio = StringIO() code_deparse(f.__code__, out=sio) func_body_codestr = sio.getvalue() # `func_body_codestr` has no info of function head, # thus we should get the header manually. signature = get_signature(f) # for Python 3.6-, we should get the # correct order of function parameters. varnames = f.__code__.co_varnames params = sorted(signature.parameters.items(), key=lambda pair: varnames.index(pair[0])) # Now, note that not all default value of a parameter # can be represented(via `repr`) directly. Say, # # ``` # class S: pass # def f(a=S()): # pass # ``` # # in above codes you just cannot deparse the code object # into source code like # `def f(a=<__main__.S object at 0x7f8c8c1692e8>): ... # # Also similar issues get raised up if any free variable here. # # As a result, please check my following codes for resolutions. freevars = {} for (name, param) in params: can_have_objects = ("default", "annotation") for obj_name in can_have_objects: obj = getattr(param, obj_name) if obj is not empty: # mangling here var_name = "_%s_%d" % (mangle, len(freevars)) freevars[var_name] = obj setattr(param, "_" + obj_name, Var(var_name)) for name, freevar in zip(f.__code__.co_freevars, f.__closure__ or ()): freevars[name] = freevar.cell_contents # the function header header = "def {name}{sig}:".format(name=f.__name__, sig=str(signature)) func_def_codestr = header + "\n" + indent(func_body_codestr, prefix=" " * 2) fn_ast = ast.parse(func_def_codestr).body[0] if debug: print_ast(fn_ast) # perform your transformation on the function's AST. fn_ast = transformer(fn_ast) # debug if debug: ast.fix_missing_locations(fn_ast) print_ast(fn_ast) # Now we have all code piece for the function definition, but we # should handle the closures/default args. freevars = list(freevars.items()) ast_for_all = ast.FunctionDef( # also mangling here name=".closure_func", args=ast.arguments( args=[ ast.arg(arg=freevar_name, annotation=None) for (freevar_name, _) in freevars ], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[], ), body=[fn_ast, ast.Return(ast.Name(f.__name__, ctx=ast.Load()))], decorator_list=[], returns=None, ) ast.fix_missing_locations(ast_for_all) code = compile(ast.Module([ast_for_all]), f.__code__.co_filename, "exec") exec(code, f.__globals__) closure_func = f.__globals__['.closure_func'] del f.__globals__['.closure_func'] return closure_func(*[var for (_, var) in freevars])