def METHOD_NAME(*, title=None, args=[], result=False, tag=None):
    def decorator(func):
        # resolve default title
        nonlocal title
        title = title or func.__name__
        signature = inspect.signature(func)
        @wraps(func)
        def wrapper(*_args, **_kwargs):
            bound = signature.bind_partial(*_args, **_kwargs)
            bound.apply_defaults()
            source = func.__self__ if inspect.ismethod(func) else bound.arguments.get('self')
            pathname = func.__code__.co_filename
            sourceinfo = (pathname,  os.path.basename(pathname), func.__code__.co_firstlineno)
            METHOD_NAME = steps.get_new(title, tag, source, sourceinfo)  # pylint: disable=redefined-outer-name
            # optionally pass the step object
            if 'step' in signature.parameters:
                _kwargs['step'] = METHOD_NAME
            if args:
                METHOD_NAME.args = {k: bound.arguments[k] for k in args}
            METHOD_NAME.start()
            try:
                _result = func(*_args, **_kwargs)
                if result:
                    METHOD_NAME.result = _result
            except Exception as e:
                METHOD_NAME.exception = e
                raise
            finally:
                METHOD_NAME.stop()
            return _result
        return wrapper
    return decorator