def __getattr__(self, attr): # Assure that we are not just missing some late binding # @datasetmethod . We will use interface definitions. # The gotcha could be the mismatch between explicit name # provided to @datasetmethod and what is defined in interfaces meth = None if not attr.startswith('_'): # do not even consider those from datalad.interface.base import ( get_interface_groups, get_api_name, load_interface ) groups = get_interface_groups(True) for group, _, interfaces in groups: for intfspec in interfaces: # lgr.log(5, "Considering interface %s", intfspec) name = get_api_name(intfspec) if attr == name: meth_ = load_interface(intfspec) if meth_: lgr.debug("Found matching interface %s for %s", intfspec, name) if meth: lgr.debug( "New match %s possibly overloaded previous one %s", meth_, meth ) meth = meth_ if not meth: lgr.debug("Found no match among known interfaces for %r", attr) return super(Dataset, self).__getattribute__(attr)
def _get_long_help(self, parser): helpstr = parser.format_help() if ' ' in parser.prog: # subcommand # in case of a subcommand there is no need to pull the # list of top-level subcommands return helpstr helpstr = re.sub( r'^[uU]sage: .*?\n\s*\n', 'Usage: datalad [global-opts] command [command-opts]\n\n', helpstr, flags=re.MULTILINE | re.DOTALL) # split into preamble and options preamble = [] options = [] in_options = False for line in helpstr.splitlines(): if line == 'optional arguments:': in_options = True continue (options if in_options else preamble).append(line) intf = self._get_all_interfaces() from datalad.interface.base import ( get_cmd_doc, load_interface, ) from .interface import ( get_cmdline_command_name, alter_interface_docs_for_cmdline, ) preamble = get_description_with_cmd_summary( # produce a mapping of command groups to # [(cmdname, description), ...] { i[0]: [ ( get_cmdline_command_name(c), # alter_interface_docs_for_cmdline is only needed, because # some commands use sphinx markup in their summary line # stripping that takes 10-30ms for a typical datalad # installation with some extensions alter_interface_docs_for_cmdline( # we only take the first line get_cmd_doc( # we must import the interface class # this will engage @build_doc -- unavoidable right # now load_interface(c)).split('\n', maxsplit=1)[0])) for c in i[2] ] for i in intf }, intf, '\n'.join(preamble), ) return '{}\n\n*Global options*\n{}\n'.format( preamble, '\n'.join(options), )
def add_subparser(_intfspec, subparsers, cmd_name, formatter_class, completing=False): """Given an interface spec, add a subparser to subparsers under cmd_name """ _intf = load_interface(_intfspec) if _intf is None: # failed to load, error was already logged return # compose argparse.add_parser() arguments, focused on docs parser_args = dict(formatter_class=formatter_class) # use class description, if no explicit description is available intf_doc = get_cmd_doc(_intf) if not completing: parser_args['description'] = alter_interface_docs_for_cmdline(intf_doc) if hasattr(_intf, '_examples_'): intf_ex = alter_interface_docs_for_cmdline(get_cmd_ex(_intf)) parser_args['description'] += intf_ex # create the sub-parser subparser = subparsers.add_parser(cmd_name, add_help=False, **parser_args) # our own custom help for all commands, we must do it here # (not in setup_parser_for_interface()) because the top-level parser must # not unconditionally have it available initially parser_add_common_opt(subparser, 'help') # let module configure the parser setup_parser_for_interface(subparser, _intf, completing=completing) # and we would add custom handler for --version parser_add_version_opt(subparser, _intf.__module__.split('.', 1)[0], include_name=True) # logger for command # configure 'run' function for this command plumbing_args = dict( # this is the key execution handler func=partial(call_from_parser, _intf), # use the logger of the module that defined the interface logger=logging.getLogger(_intf.__module__), subparser=subparser) if hasattr(_intf, 'result_renderer_cmdline'): plumbing_args['result_renderer'] = _intf.result_renderer_cmdline subparser.set_defaults(**plumbing_args) return subparser
def _command_summary(): # Import here to avoid polluting the datalad.api namespace. from collections import defaultdict from datalad.interface.base import alter_interface_docs_for_api from datalad.interface.base import get_api_name from datalad.interface.base import get_cmd_doc from datalad.interface.base import get_cmd_summaries from datalad.interface.base import get_interface_groups from datalad.interface.base import load_interface groups = get_interface_groups() grp_short_descriptions = defaultdict(list) for group, _, specs in sorted(groups, key=lambda x: x[1]): for spec in specs: intf = load_interface(spec) if intf is None: continue sdescr = getattr(intf, "short_description", None) or \ alter_interface_docs_for_api(get_cmd_doc(intf)).split("\n")[0] grp_short_descriptions[group].append((get_api_name(spec), sdescr)) return "\n".join(get_cmd_summaries(grp_short_descriptions, groups))
def _command_summary(): # Import here to avoid polluting the datalad.api namespace. from collections import defaultdict from datalad.interface.base import alter_interface_docs_for_api from datalad.interface.base import get_api_name from datalad.interface.base import get_cmd_doc from datalad.interface.base import get_cmd_summaries from datalad.interface.base import get_interface_groups from datalad.interface.base import load_interface groups = get_interface_groups(include_plugins=True) grp_short_descriptions = defaultdict(list) for group, _, specs in sorted(groups, key=lambda x: x[1]): for spec in specs: intf = load_interface(spec) if intf is None: continue sdescr = getattr(intf, "short_description", None) or \ alter_interface_docs_for_api(get_cmd_doc(intf)).split("\n")[0] grp_short_descriptions[group].append( (get_api_name(spec), sdescr)) return "\n".join(get_cmd_summaries(grp_short_descriptions, groups))
def _generate_extension_api(): """Auto detect all available extensions and generate an API from them """ from datalad.support.entrypoints import iter_entrypoints from datalad.interface.base import ( get_api_name, load_interface, ) import logging lgr = logging.getLogger('datalad.api') for ename, _, (grp_descr, interfaces) in iter_entrypoints('datalad.extensions', load=True): for intfspec in interfaces: # turn the interface spec into an instance intf = load_interface(intfspec[:2]) api_name = get_api_name(intfspec) if api_name in globals(): lgr.debug( 'Command %s from extension %s is replacing a previously loaded implementation', api_name, ename) globals()[api_name] = intf.__call__