def wrp_onexceptreport(*args, **kwargs): try: #import utool #if utool.DEBUG: # print('[IN EXCPRPT] args=%r' % (args,)) # print('[IN EXCPRPT] kwargs=%r' % (kwargs,)) return func(*args, **kwargs) except Exception as ex: from utool import util_str print('ERROR occured! Reporting input to function') if keys is not None: from utool import util_inspect from utool import util_list from utool import util_dict argspec = util_inspect.get_func_argspec(func) in_kwargs_flags = [key in kwargs for key in keys] kwarg_keys = util_list.compress(keys, in_kwargs_flags) kwarg_vals = [kwargs.get(key) for key in kwarg_keys] flags = util_list.not_list(in_kwargs_flags) arg_keys = util_list.compress(keys, flags) arg_idxs = [argspec.args.index(key) for key in arg_keys] num_nodefault = len(argspec.args) - len(argspec.defaults) default_vals = (([None] * (num_nodefault)) + list(argspec.defaults)) args_ = list(args) + default_vals[len(args) + 1:] arg_vals = util_list.take(args_, arg_idxs) requested_dict = dict(util_list.flatten( [zip(kwarg_keys, kwarg_vals), zip(arg_keys, arg_vals)])) print('input dict = ' + util_str.repr4( util_dict.dict_subset(requested_dict, keys))) # (print out specific keys only) pass arg_strs = ', '.join([repr(util_str.truncate_str(str(arg))) for arg in args]) kwarg_strs = ', '.join([ util_str.truncate_str('%s=%r' % (key, val)) for key, val in six.iteritems(kwargs)]) msg = ('\nERROR: funcname=%r,\n * args=%s,\n * kwargs=%r\n' % ( meta_util_six.get_funcname(func), arg_strs, kwarg_strs)) msg += ' * len(args) = %r\n' % len(args) msg += ' * len(kwargs) = %r\n' % len(kwargs) util_dbg.printex(ex, msg, pad_stdout=True) raise
def preserve_sig(wrapper, orig_func, force=False): """ Decorates a wrapper function. It seems impossible to presever signatures in python 2 without eval (Maybe another option is to write to a temporary module?) Args: wrapper: the function wrapping orig_func to change the signature of orig_func: the original function to take the signature from References: http://emptysqua.re/blog/copying-a-python-functions-signature/ https://code.google.com/p/micheles/source/browse/decorator/src/decorator.py TODO: checkout funcsigs https://funcsigs.readthedocs.org/en/latest/ CommandLine: python -m utool.util_decor --test-preserve_sig Example: >>> # ENABLE_DOCTEST >>> import utool as ut >>> #ut.rrrr(False) >>> def myfunction(self, listinput_, arg1, *args, **kwargs): >>> " just a test function " >>> return [x + 1 for x in listinput_] >>> #orig_func = ut.take >>> orig_func = myfunction >>> wrapper = ut.accepts_scalar_input2([0])(orig_func) >>> _wrp_preserve1 = ut.preserve_sig(wrapper, orig_func, True) >>> _wrp_preserve2 = ut.preserve_sig(wrapper, orig_func, False) >>> print('_wrp_preserve2 = %r' % (_wrp_preserve1,)) >>> print('_wrp_preserve2 = %r' % (_wrp_preserve2,)) >>> print('source _wrp_preserve1 = %s' % (ut.get_func_sourcecode(_wrp_preserve1),)) >>> print('source _wrp_preserve2 = %s' % (ut.get_func_sourcecode(_wrp_preserve2)),) >>> result = str(_wrp_preserve1) >>> print(result) """ #if True: # import functools # return functools.wraps(orig_func)(wrapper) from utool._internal import meta_util_six from utool import util_str from utool import util_inspect if wrapper is orig_func: # nothing to do return orig_func orig_docstr = meta_util_six.get_funcdoc(orig_func) orig_docstr = '' if orig_docstr is None else orig_docstr orig_argspec = util_inspect.get_func_argspec(orig_func) wrap_name = meta_util_six.get_funccode(wrapper).co_name orig_name = meta_util_six.get_funcname(orig_func) # At the very least preserve info in a dictionary _utinfo = {} _utinfo['orig_func'] = orig_func _utinfo['wrap_name'] = wrap_name _utinfo['orig_name'] = orig_name _utinfo['orig_argspec'] = orig_argspec if hasattr(wrapper, '_utinfo'): parent_wrapper_utinfo = wrapper._utinfo _utinfo['parent_wrapper_utinfo'] = parent_wrapper_utinfo if hasattr(orig_func, '_utinfo'): parent_orig_utinfo = orig_func._utinfo _utinfo['parent_orig_utinfo'] = parent_orig_utinfo # environment variable is set if you are building documentation # preserve sig if building docs building_docs = os.environ.get('UTOOL_AUTOGEN_SPHINX_RUNNING', 'OFF') == 'ON' if force or SIG_PRESERVE or building_docs: # PRESERVES ALL SIGNATURES WITH EXECS src_fmt = r''' def _wrp_preserve{defsig}: """ {orig_docstr} """ try: return wrapper{callsig} except Exception as ex: import utool as ut msg = ('Failure in signature preserving wrapper:\n') ut.printex(ex, msg) raise ''' # Put wrapped function into a scope globals_ = {'wrapper': wrapper} locals_ = {} # argspec is :ArgSpec(args=['bar', 'baz'], varargs=None, keywords=None, # defaults=(True,)) # get orig functions argspec # get functions signature # Get function call signature (no defaults) # Define an exec function argspec = inspect.getargspec(orig_func) (args, varargs, varkw, defaults) = argspec defsig = inspect.formatargspec(*argspec) callsig = inspect.formatargspec(*argspec[0:3]) # TODO: # ut.func_defsig # ut.func_callsig src_fmtdict = dict(defsig=defsig, callsig=callsig, orig_docstr=orig_docstr) src = textwrap.dedent(src_fmt).format(**src_fmtdict) # Define the new function on the fly # (I wish there was a non exec / eval way to do this) #print(src) code = compile(src, '<string>', 'exec') six.exec_(code, globals_, locals_) #six.exec_(src, globals_, locals_) # Use functools.update_wapper to complete preservation _wrp_preserve = functools.update_wrapper(locals_['_wrp_preserve'], orig_func) # Keep debug info _utinfo['src'] = src # Set an internal sig variable that we may use #_wrp_preserve.__sig__ = defsig else: # PRESERVES SOME SIGNATURES NO EXEC # signature preservation is turned off. just preserve the name. # Does not use any exec or eval statments. _wrp_preserve = functools.update_wrapper(wrapper, orig_func) # Just do something to preserve signature DEBUG_WRAPPED_DOCSTRING = False if DEBUG_WRAPPED_DOCSTRING: new_docstr_fmtstr = util_str.codeblock( ''' Wrapped function {wrap_name}({orig_name}) orig_argspec = {orig_argspec} orig_docstr = {orig_docstr} ''' ) else: new_docstr_fmtstr = util_str.codeblock( ''' {orig_docstr} ''' ) new_docstr = new_docstr_fmtstr.format( wrap_name=wrap_name, orig_name=orig_name, orig_docstr=orig_docstr, orig_argspec=orig_argspec) meta_util_six.set_funcdoc(_wrp_preserve, new_docstr) _wrp_preserve._utinfo = _utinfo return _wrp_preserve