def generic_result_renderer(res): if res.get('status', None) != 'notneeded': path = res.get('path', None) if path and res.get('refds'): try: path = relpath(path, res['refds']) except ValueError: # can happen, e.g., on windows with paths from different # drives. just go with the original path in this case pass ui.message('{action}({status}):{path}{type}{msg}{err}'.format( action=ac.color_word(res.get('action', '<action-unspecified>'), ac.BOLD), status=ac.color_status(res.get('status', '<status-unspecified>')), path=' {}'.format(path) if path else '', type=' ({})'.format(ac.color_word(res['type'], ac.MAGENTA)) if 'type' in res else '', msg=' [{}]'.format(res['message'][0] % res['message'][1:] if isinstance( res['message'], tuple) else res['message']) if res.get('message', None) else '', err=ac.color_word( ' [{}]'.format(res['error_message'][0] % res['error_message'][1:] if isinstance( res['error_message'], tuple ) else res['error_message']), ac.RED) if res.get('error_message', None) and res.get('status', None) != 'ok' else ''))
def default_result_renderer(res): if res.get('status', None) != 'notneeded': ui.message('{action}({status}): {path}{type}{msg}'.format( action=ac.color_word(res['action'], ac.BOLD), status=ac.color_status(res['status']), path=relpath(res['path'], res['refds']) if res.get('refds', None) else res['path'], type=' ({})'.format(ac.color_word(res['type'], ac.MAGENTA)) if 'type' in res else '', msg=' [{}]'.format(res['message'][0] % res['message'][1:] if isinstance( res['message'], tuple) else res['message']) if 'message' in res else ''))
def test_color_status(): # status -> (plain, colored) statuses = { 'ok': ('ok', '\033[1;32mok\033[0m'), 'notneeded': ('notneeded', '\033[1;32mnotneeded\033[0m'), 'impossible': ('impossible', '\033[1;33mimpossible\033[0m'), 'error': ('error', '\033[1;31merror\033[0m'), 'invalid': ('invalid', 'invalid'), } for enabled in (True, False): with patch('datalad.support.ansi_colors.color_enabled', lambda: enabled): for status, retopts in statuses.items(): assert_equal(colors.color_status(status), retopts[enabled])
def default_result_renderer(res): if res.get('status', None) != 'notneeded': ui.message('{action}({status}): {path}{type}{msg}'.format( action=ac.color_word(res['action'], ac.BOLD), status=ac.color_status(res['status']), path=relpath(res['path'], res['refds']) if res.get('refds', None) else res[ 'path'], type=' ({})'.format( ac.color_word(res['type'], ac.MAGENTA) ) if 'type' in res else '', msg=' [{}]'.format( res['message'][0] % res['message'][1:] if isinstance(res['message'], tuple) else res[ 'message']) if 'message' in res else ''))
def _process_results( results, cmd_class, action_summary, on_failure, incomplete_results, result_renderer, result_xfm, result_filter, **kwargs): # private helper pf @eval_results # loop over results generated from some source and handle each # of them according to the requested behavior (logging, rendering, ...) for res in results: if not res or 'action' not in res: # XXX Yarik has to no clue on how to track the origin of the # record to figure out WTF, so he just skips it continue if PY2: for k, v in res.items(): if isinstance(v, unicode): res[k] = v.encode('utf-8') actsum = action_summary.get(res['action'], {}) if res['status']: actsum[res['status']] = actsum.get(res['status'], 0) + 1 action_summary[res['action']] = actsum ## log message, if a logger was given # remove logger instance from results, as it is no longer useful # after logging was done, it isn't serializable, and generally # pollutes the output res_lgr = res.pop('logger', None) if isinstance(res_lgr, logging.Logger): # didn't get a particular log function, go with default res_lgr = getattr(res_lgr, default_logchannels[res['status']]) if res_lgr and 'message' in res: msg = res['message'] msgargs = None if isinstance(msg, tuple): msgargs = msg[1:] msg = msg[0] if 'path' in res: msg = '{} [{}({})]'.format( msg, res['action'], res['path']) if msgargs: # support string expansion of logging to avoid runtime cost res_lgr(msg, *msgargs) else: res_lgr(msg) ## error handling # looks for error status, and report at the end via # an exception if on_failure in ('continue', 'stop') \ and res['status'] in ('impossible', 'error'): incomplete_results.append(res) if on_failure == 'stop': # first fail -> that's it # raise will happen after the loop break if result_filter: try: if not result_filter(res): raise ValueError('excluded by filter') except ValueError as e: lgr.debug('not reporting result (%s)', exc_str(e)) continue ## output rendering # TODO RF this in a simple callable that gets passed into this function if result_renderer is None or result_renderer == 'disabled': pass elif result_renderer == 'default': # TODO have a helper that can expand a result message ui.message('{action}({status}): {path}{type}{msg}'.format( action=ac.color_word(res['action'], ac.BOLD), status=ac.color_status(res['status']), path=relpath(res['path'], res['refds']) if res.get('refds', None) else res['path'], type=' ({})'.format( ac.color_word(res['type'], ac.MAGENTA) ) if 'type' in res else '', msg=' [{}]'.format( res['message'][0] % res['message'][1:] if isinstance(res['message'], tuple) else res['message']) if 'message' in res else '')) elif result_renderer in ('json', 'json_pp'): ui.message(json.dumps( {k: v for k, v in res.items() if k not in ('message', 'logger')}, sort_keys=True, indent=2 if result_renderer.endswith('_pp') else None)) elif result_renderer == 'tailored': if hasattr(cmd_class, 'custom_result_renderer'): cmd_class.custom_result_renderer(res, **kwargs) elif hasattr(result_renderer, '__call__'): try: result_renderer(res, **kwargs) except Exception as e: lgr.warn('Result rendering failed for: %s [%s]', res, exc_str(e)) else: raise ValueError('unknown result renderer "{}"'.format(result_renderer)) if PY2: for k, v in res.items(): if isinstance(v, str): res[k] = v.decode('utf-8') if result_xfm: res = result_xfm(res) if res is None: continue yield res