Source code for datalad.cli.exec

"""Call a command interface

Provide a callable to register in a cmdline parser, for executing
a parameterized command call.
"""

# ATTN!
# Top-level inputs should be minimized. This module must be imported
# for parser construction, but the key function call_from_parser()
# is only executed when a command is actually engaged -- but not
# for a help action.
# Therefore no additional top-level imports beyond those already
# caused unconditionally by .main, and .parser.

from datalad import cfg

from datalad.interface.base import (
    is_api_arg,
)
from datalad.utils import (
    getargspec,
)

# only imported during command execution
# .interface._has_eval_results_call
# from .utils import EnsureKeyChoice

# special-case imports
# .renderer.DefaultOutputRenderer
# from datalad.ui import ui
# from .exceptions import CapturedException


[docs]def call_from_parser(cls, args): """Executable to be registered with the parser for a particular command Parameters ---------- cls : Interface Class implementing a particular interface. args : Namespace Populated argparse namespace instance. Returns ------- iterable Returns the iterable return by an command's implementation of ``__call__()``. It is unwound, in case of a generator being returned to actually trigger the underlying processing. """ # XXX needs safety check for name collisions import inspect from datalad.interface.base import _has_eval_results_call argspec = getargspec(cls.__call__, include_kwonlyargs=True) if argspec.keywords is None: # no **kwargs in the call receiver, pull argnames from signature argnames = argspec.args else: # common options # XXX define or better get from elsewhere # ultimately .common_args.common_args could be used, but # it is presently unclear what is being excluded here (incomplete set) common_opts = ('change_path', 'common_debug', 'common_idebug', 'func', 'help', 'log_level', 'logger', 'result_renderer', 'subparser') argnames = [name for name in dir(args) if not (name.startswith('_') or name in common_opts)] kwargs = {k: getattr(args, k) for k in argnames # some arguments might be Python-only and do not appear in the # parser Namespace if hasattr(args, k) and is_api_arg(k)} # we are coming from the entry point, this is the toplevel command, # let it run like generator so we can act on partial results quicker # TODO remove following condition test when transition is complete and # run indented code unconditionally if _has_eval_results_call(cls): # set all common args explicitly to override class defaults # that are tailored towards the the Python API kwargs['return_type'] = 'generator' kwargs['result_xfm'] = None # allow commands to override the default, unless something other # than the default 'tailored' is requested kwargs['result_renderer'] = \ args.common_result_renderer \ if args.common_result_renderer != 'tailored' \ else getattr(cls, 'result_renderer', 'generic') if '{' in args.common_result_renderer: from .renderer import DefaultOutputRenderer # stupid hack, could and should become more powerful kwargs['result_renderer'] = DefaultOutputRenderer( args.common_result_renderer) if args.common_on_failure: kwargs['on_failure'] = args.common_on_failure # compose filter function from to be invented cmdline options res_filter = _get_result_filter(args) if res_filter is not None: # Don't add result_filter if it's None because then # eval_results can't distinguish between --report-{status,type} # not specified via the CLI and None passed via the Python API. kwargs['result_filter'] = res_filter ret = cls.__call__(**kwargs) if inspect.isgenerator(ret): ret = list(ret) return ret
def _get_result_filter(args): from datalad.support.constraints import EnsureKeyChoice result_filter = None if args.common_report_status or 'datalad.runtime.report-status' in cfg: report_status = args.common_report_status or \ cfg.obtain('datalad.runtime.report-status') if report_status == "all": pass # no filter elif report_status == 'success': result_filter = EnsureKeyChoice('status', ('ok', 'notneeded')) elif report_status == 'failure': result_filter = EnsureKeyChoice('status', ('impossible', 'error')) else: result_filter = EnsureKeyChoice('status', (report_status,)) if args.common_report_type: tfilt = EnsureKeyChoice('type', tuple(args.common_report_type)) result_filter = result_filter & tfilt if result_filter else tfilt return result_filter