{
    "file_diffs": [
        {
            "old_file_content": "# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-\n# ex: set sts=4 ts=4 sw=4 noet:\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n#\n#   See COPYING file distributed along with the datalad package for the\n#   copyright and license terms.\n#\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n\"\"\"High-level interface generation\n\n\"\"\"\n\n__docformat__ = 'restructuredtext'\n\nimport logging\nlgr = logging.getLogger('datalad.interface.base')\n\nimport sys\nimport re\nimport textwrap\nimport inspect\nfrom collections import OrderedDict\n\nfrom ..ui import ui\nfrom ..dochelpers import exc_str\n\nfrom datalad.interface.common_opts import eval_params\nfrom datalad.interface.common_opts import eval_defaults\nfrom datalad.support.constraints import EnsureKeyChoice\nfrom datalad.distribution.dataset import Dataset\nfrom datalad.distribution.dataset import resolve_path\n\n\ndefault_logchannels = {\n    '': 'debug',\n    'ok': 'debug',\n    'notneeded': 'debug',\n    'impossible': 'warning',\n    'error': 'error',\n}\n\n\ndef get_api_name(intfspec):\n    \"\"\"Given an interface specification return an API name for it\"\"\"\n    if len(intfspec) > 3:\n        name = intfspec[3]\n    else:\n        name = intfspec[0].split('.')[-1]\n    return name\n\n\ndef get_cmdline_command_name(intfspec):\n    \"\"\"Given an interface specification return a cmdline command name\"\"\"\n    if len(intfspec) > 2:\n        name = intfspec[2]\n    else:\n        name = intfspec[0].split('.')[-1].replace('_', '-')\n    return name\n\n\ndef get_interface_groups():\n    from .. import interface as _interfaces\n\n    grps = []\n    # auto detect all available interfaces and generate a function-based\n    # API from them\n    for _item in _interfaces.__dict__:\n        if not _item.startswith('_group_'):\n            continue\n        grp_name = _item[7:]\n        grp = getattr(_interfaces, _item)\n        grps.append((grp_name,) + grp)\n    return grps\n\n\ndef dedent_docstring(text):\n    \"\"\"Remove uniform indentation from a multiline docstring\"\"\"\n    # Problem is that first line might often have no offset, so might\n    # need to be ignored from dedent call\n    if text is None:\n        return None\n    if not text.startswith(' '):\n        lines = text.split('\\n')\n        if len(lines) == 1:\n            # single line, no indentation, nothing to do\n            return text\n        text2 = '\\n'.join(lines[1:])\n        return lines[0] + \"\\n\" + textwrap.dedent(text2)\n    else:\n        return textwrap.dedent(text)\n\n\ndef alter_interface_docs_for_api(docs):\n    \"\"\"Apply modifications to interface docstrings for Python API use.\"\"\"\n    # central place to alter the impression of docstrings,\n    # like removing cmdline specific sections\n    if not docs:\n        return docs\n    docs = dedent_docstring(docs)\n    # clean cmdline sections\n    docs = re.sub(\n        '\\|\\| CMDLINE \\>\\>.*\\<\\< CMDLINE \\|\\|',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # clean cmdline in-line bits\n    docs = re.sub(\n        '\\[CMD:\\s[^\\[\\]]*\\sCMD\\]',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    docs = re.sub(\n        '\\[PY:\\s([^\\[\\]]*)\\sPY\\]',\n        lambda match: match.group(1),\n        docs,\n        flags=re.MULTILINE)\n    docs = re.sub(\n        '\\|\\| PYTHON \\>\\>(.*)\\<\\< PYTHON \\|\\|',\n        lambda match: match.group(1),\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    docs = re.sub(\n        '\\|\\| REFLOW \\>\\>\\n(.*)\\<\\< REFLOW \\|\\|',\n        lambda match: textwrap.fill(match.group(1)),\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    return docs\n\n\ndef alter_interface_docs_for_cmdline(docs):\n    \"\"\"Apply modifications to interface docstrings for cmdline doc use.\"\"\"\n    # central place to alter the impression of docstrings,\n    # like removing Python API specific sections, and argument markup\n    if not docs:\n        return docs\n    docs = dedent_docstring(docs)\n    # clean cmdline sections\n    docs = re.sub(\n        '\\|\\| PYTHON \\>\\>.*\\<\\< PYTHON \\|\\|',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # clean cmdline in-line bits\n    docs = re.sub(\n        '\\[PY:\\s[^\\[\\]]*\\sPY\\]',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    docs = re.sub(\n        '\\[CMD:\\s([^\\[\\]]*)\\sCMD\\]',\n        lambda match: match.group(1),\n        docs,\n        flags=re.MULTILINE)\n    docs = re.sub(\n        '\\|\\| CMDLINE \\>\\>(.*)\\<\\< CMDLINE \\|\\|',\n        lambda match: match.group(1),\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # remove :role:`...` RST markup for cmdline docs\n    docs = re.sub(\n        r':\\S+:`[^`]*`[\\\\]*',\n        lambda match: ':'.join(match.group(0).split(':')[2:]).strip('`\\\\'),\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # remove None constraint. In general, `None` on the cmdline means don't\n    # give option at all, but specifying `None` explicitly is practically\n    # impossible\n    docs = re.sub(\n        ',\\sor\\svalue\\smust\\sbe\\s`None`',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # capitalize variables and remove backticks to uniformize with\n    # argparse output\n    docs = re.sub(\n        '`\\S*`',\n        lambda match: match.group(0).strip('`').upper(),\n        docs)\n    # clean up sphinx API refs\n    docs = re.sub(\n        '\\~datalad\\.api\\.\\S*',\n        lambda match: \"`{0}`\".format(match.group(0)[13:]),\n        docs)\n    # Remove RST paragraph markup\n    docs = re.sub(\n        r'^.. \\S+::',\n        lambda match: match.group(0)[3:-2].upper(),\n        docs,\n        flags=re.MULTILINE)\n    docs = re.sub(\n        '\\|\\| REFLOW \\>\\>\\n(.*)\\<\\< REFLOW \\|\\|',\n        lambda match: textwrap.fill(match.group(1)),\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    return docs\n\n\ndef is_api_arg(arg):\n    \"\"\"Return True if argument is our API argument or self or used for internal\n    purposes\n    \"\"\"\n    return arg != 'self' and not arg.startswith('_')\n\n\ndef update_docstring_with_parameters(func, params, prefix=None, suffix=None,\n                                     add_args=None):\n    \"\"\"Generate a useful docstring from a parameter spec\n\n    Amends any existing docstring of a callable with a textual\n    description of its parameters. The Parameter spec needs to match\n    the number and names of the callables arguments.\n    \"\"\"\n    from inspect import getargspec\n    # get the signature\n    ndefaults = 0\n    args, varargs, varkw, defaults = getargspec(func)\n    if add_args:\n        add_argnames = sorted(add_args.keys())\n        args.extend(add_argnames)\n        defaults = defaults + tuple(add_args[k] for k in add_argnames)\n    if defaults is not None:\n        ndefaults = len(defaults)\n    # start documentation with what the callable brings with it\n    doc = prefix if prefix else u''\n    if len(args) > 1:\n        if len(doc):\n            doc += '\\n'\n        doc += \"Parameters\\n----------\\n\"\n        for i, arg in enumerate(args):\n            if not is_api_arg(arg):\n                continue\n            # we need a parameter spec for each argument\n            if not arg in params:\n                raise ValueError(\"function has argument '%s' not described as a parameter\" % arg)\n            param = params[arg]\n            # validate the default -- to make sure that the parameter description is\n            # somewhat OK\n            defaults_idx = ndefaults - len(args) + i\n            if defaults_idx >= 0:\n                if param.constraints is not None:\n                    param.constraints(defaults[defaults_idx])\n            orig_docs = param._doc\n            param._doc = alter_interface_docs_for_api(param._doc)\n            doc += param.get_autodoc(\n                arg,\n                default=defaults[defaults_idx] if defaults_idx >= 0 else None,\n                has_default=defaults_idx >= 0)\n            param._doc = orig_docs\n            doc += '\\n'\n    doc += suffix if suffix else u\"\"\n    # assign the amended docs\n    func.__doc__ = doc\n    return func\n\n\ndef build_doc(cls, **kwargs):\n    \"\"\"Decorator to build docstrings for datalad commands\n\n    It's intended to decorate the class, the __call__-method of which is the\n    actual command. It expects that __call__-method to be decorated by\n    eval_results.\n\n    Parameters\n    ----------\n    cls: Interface\n      class defining a datalad command\n    \"\"\"\n\n    # Note, that this is a class decorator, which is executed only once when the\n    # class is imported. It builds the docstring for the class' __call__ method\n    # and returns the original class.\n    #\n    # This is because a decorator for the actual function would not be able to\n    # behave like this. To build the docstring we need to access the attribute\n    # _params of the class. From within a function decorator we cannot do this\n    # during import time, since the class is being built in this very moment and\n    # is not yet available in the module. And if we do it from within the part\n    # of a function decorator, that is executed when the function is called, we\n    # would need to actually call the command once in order to build this\n    # docstring.\n\n    lgr.debug(\"Building doc for {}\".format(cls))\n\n    cls_doc = cls.__doc__\n    if hasattr(cls, '_docs_'):\n        # expand docs\n        cls_doc = cls_doc.format(**cls._docs_)\n\n    call_doc = None\n    # suffix for update_docstring_with_parameters:\n    if cls.__call__.__doc__:\n        call_doc = cls.__call__.__doc__\n\n    # build standard doc and insert eval_doc\n    spec = getattr(cls, '_params_', dict())\n    # get docs for eval_results parameters:\n    spec.update(eval_params)\n\n    update_docstring_with_parameters(\n        cls.__call__, spec,\n        prefix=alter_interface_docs_for_api(cls_doc),\n        suffix=alter_interface_docs_for_api(call_doc),\n        add_args=eval_defaults if not hasattr(cls, '_no_eval_results') else None\n    )\n\n    # return original\n    return cls\n\n\nclass Interface(object):\n    \"\"\"Base class for interface implementations\"\"\"\n\n    # exit code to return if user-interrupted\n    # if None, would just reraise the Exception, so if in --dbg\n    # mode would fall into the debugger\n    _interrupted_exit_code = 1\n\n    _OLDSTYLE_COMMANDS = (\n        'AddArchiveContent', 'CrawlInit', 'Crawl', 'CreateSiblingGithub',\n        'CreateTestDataset', 'DownloadURL', 'Export', 'Ls', 'Move', 'SSHRun',\n        'Test')\n\n    @classmethod\n    def setup_parser(cls, parser):\n        # XXX needs safety check for name collisions\n        # XXX allow for parser kwargs customization\n        parser_kwargs = {}\n        from inspect import getargspec\n        # get the signature\n        ndefaults = 0\n        args, varargs, varkw, defaults = getargspec(cls.__call__)\n        if not defaults is None:\n            ndefaults = len(defaults)\n        for i, arg in enumerate(args):\n            if not is_api_arg(arg):\n                continue\n            param = cls._params_[arg]\n            defaults_idx = ndefaults - len(args) + i\n            cmd_args = param.cmd_args\n            if cmd_args is None:\n                cmd_args = []\n            if not len(cmd_args):\n                if defaults_idx >= 0:\n                    # dealing with a kwarg\n                    template = '--%s'\n                else:\n                    # positional arg\n                    template = '%s'\n                # use parameter name as default argument name\n                parser_args = (template % arg.replace('_', '-'),)\n            else:\n                parser_args = [c.replace('_', '-') for c in cmd_args]\n            parser_kwargs = param.cmd_kwargs\n            if defaults_idx >= 0:\n                parser_kwargs['default'] = defaults[defaults_idx]\n            help = alter_interface_docs_for_cmdline(param._doc)\n            if help and help[-1] != '.':\n                help += '.'\n            if param.constraints is not None:\n                parser_kwargs['type'] = param.constraints\n                # include value constraint description and default\n                # into the help string\n                cdoc = alter_interface_docs_for_cmdline(\n                    param.constraints.long_description())\n                if cdoc[0] == '(' and cdoc[-1] == ')':\n                    cdoc = cdoc[1:-1]\n                help += '  Constraints: %s' % cdoc\n            if defaults_idx >= 0:\n                help += \" [Default: %r]\" % (defaults[defaults_idx],)\n            # create the parameter, using the constraint instance for type\n            # conversion\n            parser.add_argument(*parser_args, help=help,\n                                **parser_kwargs)\n\n    @classmethod\n    def call_from_parser(cls, args):\n        # XXX needs safety check for name collisions\n        from inspect import getargspec\n        argspec = getargspec(cls.__call__)\n        if argspec[2] is None:\n            # no **kwargs in the call receiver, pull argnames from signature\n            argnames = getargspec(cls.__call__)[0]\n        else:\n            # common options\n            # XXX define or better get from elsewhere\n            common_opts = ('change_path', 'common_debug', 'common_idebug', 'func',\n                           'help', 'log_level', 'logger', 'pbs_runner',\n                           'result_renderer', 'subparser')\n            argnames = [name for name in dir(args)\n                        if not (name.startswith('_') or name in common_opts)]\n        kwargs = {k: getattr(args, k) for k in argnames if is_api_arg(k)}\n        # we are coming from the entry point, this is the toplevel command,\n        # let it run like generator so we can act on partial results quicker\n        # TODO remove following condition test when transition is complete and\n        # run indented code unconditionally\n        if cls.__name__ not in Interface._OLDSTYLE_COMMANDS:\n            # set all common args explicitly  to override class defaults\n            # that are tailored towards the the Python API\n            kwargs['return_type'] = 'generator'\n            kwargs['result_xfm'] = None\n            # allow commands to override the default, unless something other than\n            # default is requested\n            kwargs['result_renderer'] = \\\n                args.common_output_format if args.common_output_format != 'default' \\\n                else getattr(cls, 'result_renderer', args.common_output_format)\n            if '{' in args.common_output_format:\n                # stupid hack, could and should become more powerful\n                kwargs['result_renderer'] = \\\n                    lambda x, **kwargs: ui.message(args.common_output_format.format(\n                        **{k: {k_.replace(':', '#'): v_ for k_, v_ in v.items()}\n                           if isinstance(v, dict) else v\n                           for k, v in x.items()}))\n            if args.common_on_failure:\n                kwargs['on_failure'] = args.common_on_failure\n            # compose filter function from to be invented cmdline options\n            res_filter = cls._get_result_filter(args)\n            if res_filter is not None:\n                # Don't add result_filter if it's None because then\n                # eval_results can't distinguish between --report-{status,type}\n                # not specified via the CLI and None passed via the Python API.\n                kwargs['result_filter'] = res_filter\n        try:\n            ret = cls.__call__(**kwargs)\n            if inspect.isgenerator(ret):\n                ret = list(ret)\n            if args.common_output_format == 'tailored' and \\\n                    hasattr(cls, 'custom_result_summary_renderer'):\n                cls.custom_result_summary_renderer(ret)\n            return ret\n        except KeyboardInterrupt as exc:\n            ui.error(\"\\nInterrupted by user while doing magic: %s\" % exc_str(exc))\n            if cls._interrupted_exit_code is not None:\n                sys.exit(cls._interrupted_exit_code)\n            else:\n                raise\n\n    @classmethod\n    def _get_result_filter(cls, args):\n        from datalad import cfg\n        result_filter = None\n        if args.common_report_status or 'datalad.runtime.report-status' in cfg:\n            report_status = args.common_report_status or \\\n                            cfg.obtain('datalad.runtime.report-status')\n            if report_status == \"all\":\n                pass  # no filter\n            elif report_status == 'success':\n                result_filter = EnsureKeyChoice('status', ('ok', 'notneeded'))\n            elif report_status == 'failure':\n                result_filter = EnsureKeyChoice('status',\n                                                ('impossible', 'error'))\n            else:\n                result_filter = EnsureKeyChoice('status', (report_status,))\n        if args.common_report_type:\n            tfilt = EnsureKeyChoice('type', tuple(args.common_report_type))\n            result_filter = result_filter & tfilt if result_filter else tfilt\n        return result_filter\n\n    @classmethod\n    def get_refds_path(cls, dataset):\n        \"\"\"Return a resolved reference dataset path from a `dataset` argument\"\"\"\n        # theoretically a dataset could come in as a relative path -> resolve\n        if dataset is None:\n            return dataset\n        refds_path = dataset.path if isinstance(dataset, Dataset) \\\n            else Dataset(dataset).path\n        if refds_path:\n            refds_path = resolve_path(refds_path)\n        return refds_path\n\n\ndef get_allargs_as_kwargs(call, args, kwargs):\n    \"\"\"Generate a kwargs dict from a call signature and *args, **kwargs\n\n    Basically resolving the argnames for all positional arguments, and\n    resolvin the defaults for all kwargs that are not given in a kwargs\n    dict\n    \"\"\"\n    from inspect import getargspec\n    argspec = getargspec(call)\n    defaults = argspec.defaults\n    nargs = len(argspec.args)\n    assert (nargs >= len(defaults))\n    # map any args to their name\n    argmap = list(zip(argspec.args[:len(args)], args))\n    kwargs_ = OrderedDict(argmap)\n    # map defaults of kwargs to their names (update below)\n    for k, v in zip(argspec.args[-len(defaults):], defaults):\n        if k not in kwargs_:\n            kwargs_[k] = v\n    # update with provided kwarg args\n    kwargs_.update(kwargs)\n    # XXX we cannot assert the following, because our own highlevel\n    # API commands support more kwargs than what is discoverable\n    # from their signature...\n    #assert (nargs == len(kwargs_))\n    return kwargs_\n",
            "new_file_content": "# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-\n# ex: set sts=4 ts=4 sw=4 noet:\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n#\n#   See COPYING file distributed along with the datalad package for the\n#   copyright and license terms.\n#\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n\"\"\"High-level interface generation\n\n\"\"\"\n\n__docformat__ = 'restructuredtext'\n\nimport logging\nlgr = logging.getLogger('datalad.interface.base')\n\nimport sys\nimport re\nimport textwrap\nimport inspect\nfrom collections import OrderedDict\n\nfrom ..ui import ui\nfrom ..dochelpers import exc_str\n\nfrom datalad.interface.common_opts import eval_params\nfrom datalad.interface.common_opts import eval_defaults\nfrom datalad.support.constraints import EnsureKeyChoice\nfrom datalad.distribution.dataset import Dataset\nfrom datalad.distribution.dataset import resolve_path\n\n\ndefault_logchannels = {\n    '': 'debug',\n    'ok': 'debug',\n    'notneeded': 'debug',\n    'impossible': 'warning',\n    'error': 'error',\n}\n\n\ndef get_api_name(intfspec):\n    \"\"\"Given an interface specification return an API name for it\"\"\"\n    if len(intfspec) > 3:\n        name = intfspec[3]\n    else:\n        name = intfspec[0].split('.')[-1]\n    return name\n\n\ndef get_cmdline_command_name(intfspec):\n    \"\"\"Given an interface specification return a cmdline command name\"\"\"\n    if len(intfspec) > 2:\n        name = intfspec[2]\n    else:\n        name = intfspec[0].split('.')[-1].replace('_', '-')\n    return name\n\n\ndef get_interface_groups():\n    from .. import interface as _interfaces\n\n    grps = []\n    # auto detect all available interfaces and generate a function-based\n    # API from them\n    for _item in _interfaces.__dict__:\n        if not _item.startswith('_group_'):\n            continue\n        grp_name = _item[7:]\n        grp = getattr(_interfaces, _item)\n        grps.append((grp_name,) + grp)\n    return grps\n\n\ndef dedent_docstring(text):\n    \"\"\"Remove uniform indentation from a multiline docstring\"\"\"\n    # Problem is that first line might often have no offset, so might\n    # need to be ignored from dedent call\n    if text is None:\n        return None\n    if not text.startswith(' '):\n        lines = text.split('\\n')\n        if len(lines) == 1:\n            # single line, no indentation, nothing to do\n            return text\n        text2 = '\\n'.join(lines[1:])\n        return lines[0] + \"\\n\" + textwrap.dedent(text2)\n    else:\n        return textwrap.dedent(text)\n\n\ndef alter_interface_docs_for_api(docs):\n    \"\"\"Apply modifications to interface docstrings for Python API use.\"\"\"\n    # central place to alter the impression of docstrings,\n    # like removing cmdline specific sections\n    if not docs:\n        return docs\n    docs = dedent_docstring(docs)\n    # clean cmdline sections\n    docs = re.sub(\n        '\\|\\| CMDLINE \\>\\>.*?\\<\\< CMDLINE \\|\\|',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # clean cmdline in-line bits\n    docs = re.sub(\n        '\\[CMD:\\s[^\\[\\]]*\\sCMD\\]',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    docs = re.sub(\n        '\\[PY:\\s([^\\[\\]]*)\\sPY\\]',\n        lambda match: match.group(1),\n        docs,\n        flags=re.MULTILINE)\n    docs = re.sub(\n        '\\|\\| PYTHON \\>\\>(.*?)\\<\\< PYTHON \\|\\|',\n        lambda match: match.group(1),\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    docs = re.sub(\n        '\\|\\| REFLOW \\>\\>\\n(.*?)\\<\\< REFLOW \\|\\|',\n        lambda match: textwrap.fill(match.group(1)),\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    return docs\n\n\ndef alter_interface_docs_for_cmdline(docs):\n    \"\"\"Apply modifications to interface docstrings for cmdline doc use.\"\"\"\n    # central place to alter the impression of docstrings,\n    # like removing Python API specific sections, and argument markup\n    if not docs:\n        return docs\n    docs = dedent_docstring(docs)\n    # clean cmdline sections\n    docs = re.sub(\n        '\\|\\| PYTHON \\>\\>.*?\\<\\< PYTHON \\|\\|',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # clean cmdline in-line bits\n    docs = re.sub(\n        '\\[PY:\\s[^\\[\\]]*\\sPY\\]',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    docs = re.sub(\n        '\\[CMD:\\s([^\\[\\]]*)\\sCMD\\]',\n        lambda match: match.group(1),\n        docs,\n        flags=re.MULTILINE)\n    docs = re.sub(\n        '\\|\\| CMDLINE \\>\\>(.*?)\\<\\< CMDLINE \\|\\|',\n        lambda match: match.group(1),\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # remove :role:`...` RST markup for cmdline docs\n    docs = re.sub(\n        r':\\S+:`[^`]*`[\\\\]*',\n        lambda match: ':'.join(match.group(0).split(':')[2:]).strip('`\\\\'),\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # remove None constraint. In general, `None` on the cmdline means don't\n    # give option at all, but specifying `None` explicitly is practically\n    # impossible\n    docs = re.sub(\n        ',\\sor\\svalue\\smust\\sbe\\s`None`',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # capitalize variables and remove backticks to uniformize with\n    # argparse output\n    docs = re.sub(\n        '`\\S*`',\n        lambda match: match.group(0).strip('`').upper(),\n        docs)\n    # clean up sphinx API refs\n    docs = re.sub(\n        '\\~datalad\\.api\\.\\S*',\n        lambda match: \"`{0}`\".format(match.group(0)[13:]),\n        docs)\n    # Remove RST paragraph markup\n    docs = re.sub(\n        r'^.. \\S+::',\n        lambda match: match.group(0)[3:-2].upper(),\n        docs,\n        flags=re.MULTILINE)\n    docs = re.sub(\n        '\\|\\| REFLOW \\>\\>\\n(.*?)\\<\\< REFLOW \\|\\|',\n        lambda match: textwrap.fill(match.group(1)),\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    return docs\n\n\ndef is_api_arg(arg):\n    \"\"\"Return True if argument is our API argument or self or used for internal\n    purposes\n    \"\"\"\n    return arg != 'self' and not arg.startswith('_')\n\n\ndef update_docstring_with_parameters(func, params, prefix=None, suffix=None,\n                                     add_args=None):\n    \"\"\"Generate a useful docstring from a parameter spec\n\n    Amends any existing docstring of a callable with a textual\n    description of its parameters. The Parameter spec needs to match\n    the number and names of the callables arguments.\n    \"\"\"\n    from inspect import getargspec\n    # get the signature\n    ndefaults = 0\n    args, varargs, varkw, defaults = getargspec(func)\n    if add_args:\n        add_argnames = sorted(add_args.keys())\n        args.extend(add_argnames)\n        defaults = defaults + tuple(add_args[k] for k in add_argnames)\n    if defaults is not None:\n        ndefaults = len(defaults)\n    # start documentation with what the callable brings with it\n    doc = prefix if prefix else u''\n    if len(args) > 1:\n        if len(doc):\n            doc += '\\n'\n        doc += \"Parameters\\n----------\\n\"\n        for i, arg in enumerate(args):\n            if not is_api_arg(arg):\n                continue\n            # we need a parameter spec for each argument\n            if not arg in params:\n                raise ValueError(\"function has argument '%s' not described as a parameter\" % arg)\n            param = params[arg]\n            # validate the default -- to make sure that the parameter description is\n            # somewhat OK\n            defaults_idx = ndefaults - len(args) + i\n            if defaults_idx >= 0:\n                if param.constraints is not None:\n                    param.constraints(defaults[defaults_idx])\n            orig_docs = param._doc\n            param._doc = alter_interface_docs_for_api(param._doc)\n            doc += param.get_autodoc(\n                arg,\n                default=defaults[defaults_idx] if defaults_idx >= 0 else None,\n                has_default=defaults_idx >= 0)\n            param._doc = orig_docs\n            doc += '\\n'\n    doc += suffix if suffix else u\"\"\n    # assign the amended docs\n    func.__doc__ = doc\n    return func\n\n\ndef build_doc(cls, **kwargs):\n    \"\"\"Decorator to build docstrings for datalad commands\n\n    It's intended to decorate the class, the __call__-method of which is the\n    actual command. It expects that __call__-method to be decorated by\n    eval_results.\n\n    Parameters\n    ----------\n    cls: Interface\n      class defining a datalad command\n    \"\"\"\n\n    # Note, that this is a class decorator, which is executed only once when the\n    # class is imported. It builds the docstring for the class' __call__ method\n    # and returns the original class.\n    #\n    # This is because a decorator for the actual function would not be able to\n    # behave like this. To build the docstring we need to access the attribute\n    # _params of the class. From within a function decorator we cannot do this\n    # during import time, since the class is being built in this very moment and\n    # is not yet available in the module. And if we do it from within the part\n    # of a function decorator, that is executed when the function is called, we\n    # would need to actually call the command once in order to build this\n    # docstring.\n\n    lgr.debug(\"Building doc for {}\".format(cls))\n\n    cls_doc = cls.__doc__\n    if hasattr(cls, '_docs_'):\n        # expand docs\n        cls_doc = cls_doc.format(**cls._docs_)\n\n    call_doc = None\n    # suffix for update_docstring_with_parameters:\n    if cls.__call__.__doc__:\n        call_doc = cls.__call__.__doc__\n\n    # build standard doc and insert eval_doc\n    spec = getattr(cls, '_params_', dict())\n    # get docs for eval_results parameters:\n    spec.update(eval_params)\n\n    update_docstring_with_parameters(\n        cls.__call__, spec,\n        prefix=alter_interface_docs_for_api(cls_doc),\n        suffix=alter_interface_docs_for_api(call_doc),\n        add_args=eval_defaults if not hasattr(cls, '_no_eval_results') else None\n    )\n\n    # return original\n    return cls\n\n\nclass Interface(object):\n    \"\"\"Base class for interface implementations\"\"\"\n\n    # exit code to return if user-interrupted\n    # if None, would just reraise the Exception, so if in --dbg\n    # mode would fall into the debugger\n    _interrupted_exit_code = 1\n\n    _OLDSTYLE_COMMANDS = (\n        'AddArchiveContent', 'CrawlInit', 'Crawl', 'CreateSiblingGithub',\n        'CreateTestDataset', 'DownloadURL', 'Export', 'Ls', 'Move', 'SSHRun',\n        'Test')\n\n    @classmethod\n    def setup_parser(cls, parser):\n        # XXX needs safety check for name collisions\n        # XXX allow for parser kwargs customization\n        parser_kwargs = {}\n        from inspect import getargspec\n        # get the signature\n        ndefaults = 0\n        args, varargs, varkw, defaults = getargspec(cls.__call__)\n        if not defaults is None:\n            ndefaults = len(defaults)\n        for i, arg in enumerate(args):\n            if not is_api_arg(arg):\n                continue\n            param = cls._params_[arg]\n            defaults_idx = ndefaults - len(args) + i\n            cmd_args = param.cmd_args\n            if cmd_args is None:\n                cmd_args = []\n            if not len(cmd_args):\n                if defaults_idx >= 0:\n                    # dealing with a kwarg\n                    template = '--%s'\n                else:\n                    # positional arg\n                    template = '%s'\n                # use parameter name as default argument name\n                parser_args = (template % arg.replace('_', '-'),)\n            else:\n                parser_args = [c.replace('_', '-') for c in cmd_args]\n            parser_kwargs = param.cmd_kwargs\n            if defaults_idx >= 0:\n                parser_kwargs['default'] = defaults[defaults_idx]\n            help = alter_interface_docs_for_cmdline(param._doc)\n            if help and help[-1] != '.':\n                help += '.'\n            if param.constraints is not None:\n                parser_kwargs['type'] = param.constraints\n                # include value constraint description and default\n                # into the help string\n                cdoc = alter_interface_docs_for_cmdline(\n                    param.constraints.long_description())\n                if cdoc[0] == '(' and cdoc[-1] == ')':\n                    cdoc = cdoc[1:-1]\n                help += '  Constraints: %s' % cdoc\n            if defaults_idx >= 0:\n                help += \" [Default: %r]\" % (defaults[defaults_idx],)\n            # create the parameter, using the constraint instance for type\n            # conversion\n            parser.add_argument(*parser_args, help=help,\n                                **parser_kwargs)\n\n    @classmethod\n    def call_from_parser(cls, args):\n        # XXX needs safety check for name collisions\n        from inspect import getargspec\n        argspec = getargspec(cls.__call__)\n        if argspec[2] is None:\n            # no **kwargs in the call receiver, pull argnames from signature\n            argnames = getargspec(cls.__call__)[0]\n        else:\n            # common options\n            # XXX define or better get from elsewhere\n            common_opts = ('change_path', 'common_debug', 'common_idebug', 'func',\n                           'help', 'log_level', 'logger', 'pbs_runner',\n                           'result_renderer', 'subparser')\n            argnames = [name for name in dir(args)\n                        if not (name.startswith('_') or name in common_opts)]\n        kwargs = {k: getattr(args, k) for k in argnames if is_api_arg(k)}\n        # we are coming from the entry point, this is the toplevel command,\n        # let it run like generator so we can act on partial results quicker\n        # TODO remove following condition test when transition is complete and\n        # run indented code unconditionally\n        if cls.__name__ not in Interface._OLDSTYLE_COMMANDS:\n            # set all common args explicitly  to override class defaults\n            # that are tailored towards the the Python API\n            kwargs['return_type'] = 'generator'\n            kwargs['result_xfm'] = None\n            # allow commands to override the default, unless something other than\n            # default is requested\n            kwargs['result_renderer'] = \\\n                args.common_output_format if args.common_output_format != 'default' \\\n                else getattr(cls, 'result_renderer', args.common_output_format)\n            if '{' in args.common_output_format:\n                # stupid hack, could and should become more powerful\n                kwargs['result_renderer'] = \\\n                    lambda x, **kwargs: ui.message(args.common_output_format.format(\n                        **{k: {k_.replace(':', '#'): v_ for k_, v_ in v.items()}\n                           if isinstance(v, dict) else v\n                           for k, v in x.items()}))\n            if args.common_on_failure:\n                kwargs['on_failure'] = args.common_on_failure\n            # compose filter function from to be invented cmdline options\n            res_filter = cls._get_result_filter(args)\n            if res_filter is not None:\n                # Don't add result_filter if it's None because then\n                # eval_results can't distinguish between --report-{status,type}\n                # not specified via the CLI and None passed via the Python API.\n                kwargs['result_filter'] = res_filter\n        try:\n            ret = cls.__call__(**kwargs)\n            if inspect.isgenerator(ret):\n                ret = list(ret)\n            if args.common_output_format == 'tailored' and \\\n                    hasattr(cls, 'custom_result_summary_renderer'):\n                cls.custom_result_summary_renderer(ret)\n            return ret\n        except KeyboardInterrupt as exc:\n            ui.error(\"\\nInterrupted by user while doing magic: %s\" % exc_str(exc))\n            if cls._interrupted_exit_code is not None:\n                sys.exit(cls._interrupted_exit_code)\n            else:\n                raise\n\n    @classmethod\n    def _get_result_filter(cls, args):\n        from datalad import cfg\n        result_filter = None\n        if args.common_report_status or 'datalad.runtime.report-status' in cfg:\n            report_status = args.common_report_status or \\\n                            cfg.obtain('datalad.runtime.report-status')\n            if report_status == \"all\":\n                pass  # no filter\n            elif report_status == 'success':\n                result_filter = EnsureKeyChoice('status', ('ok', 'notneeded'))\n            elif report_status == 'failure':\n                result_filter = EnsureKeyChoice('status',\n                                                ('impossible', 'error'))\n            else:\n                result_filter = EnsureKeyChoice('status', (report_status,))\n        if args.common_report_type:\n            tfilt = EnsureKeyChoice('type', tuple(args.common_report_type))\n            result_filter = result_filter & tfilt if result_filter else tfilt\n        return result_filter\n\n    @classmethod\n    def get_refds_path(cls, dataset):\n        \"\"\"Return a resolved reference dataset path from a `dataset` argument\"\"\"\n        # theoretically a dataset could come in as a relative path -> resolve\n        if dataset is None:\n            return dataset\n        refds_path = dataset.path if isinstance(dataset, Dataset) \\\n            else Dataset(dataset).path\n        if refds_path:\n            refds_path = resolve_path(refds_path)\n        return refds_path\n\n\ndef get_allargs_as_kwargs(call, args, kwargs):\n    \"\"\"Generate a kwargs dict from a call signature and *args, **kwargs\n\n    Basically resolving the argnames for all positional arguments, and\n    resolvin the defaults for all kwargs that are not given in a kwargs\n    dict\n    \"\"\"\n    from inspect import getargspec\n    argspec = getargspec(call)\n    defaults = argspec.defaults\n    nargs = len(argspec.args)\n    assert (nargs >= len(defaults))\n    # map any args to their name\n    argmap = list(zip(argspec.args[:len(args)], args))\n    kwargs_ = OrderedDict(argmap)\n    # map defaults of kwargs to their names (update below)\n    for k, v in zip(argspec.args[-len(defaults):], defaults):\n        if k not in kwargs_:\n            kwargs_[k] = v\n    # update with provided kwarg args\n    kwargs_.update(kwargs)\n    # XXX we cannot assert the following, because our own highlevel\n    # API commands support more kwargs than what is discoverable\n    # from their signature...\n    #assert (nargs == len(kwargs_))\n    return kwargs_\n",
            "header": {
                "file": {
                    "path": "datalad/interface/base.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "bed8339b8",
                "new_commit_hash": "c2f351d41",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/datalad/interface/base.py"
            },
            "plus_file": {
                "path": "b/datalad/interface/base.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 99,
                            "length": 7
                        },
                        "new_range": {
                            "start": 99,
                            "length": 7
                        },
                        "section": "def alter_interface_docs_for_api(docs):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    docs = dedent_docstring(docs)",
                                "type": "context"
                            },
                            {
                                "content": "    # clean cmdline sections",
                                "type": "context"
                            },
                            {
                                "content": "    docs = re.sub(",
                                "type": "context"
                            },
                            {
                                "content": "        '\\|\\| CMDLINE \\>\\>.*\\<\\< CMDLINE \\|\\|',",
                                "type": "deleted"
                            },
                            {
                                "content": "        '\\|\\| CMDLINE \\>\\>.*?\\<\\< CMDLINE \\|\\|',",
                                "type": "added"
                            },
                            {
                                "content": "        '',",
                                "type": "context"
                            },
                            {
                                "content": "        docs,",
                                "type": "context"
                            },
                            {
                                "content": "        flags=re.MULTILINE | re.DOTALL)",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "datalad/interface/base.py",
                            "type": "function",
                            "name": "alter_interface_docs_for_api",
                            "content": "def alter_interface_docs_for_api(docs):\n    \"\"\"Apply modifications to interface docstrings for Python API use.\"\"\"\n    # central place to alter the impression of docstrings,\n    # like removing cmdline specific sections\n    if not docs:\n        return docs\n    docs = dedent_docstring(docs)\n    # clean cmdline sections\n    docs = re.sub(\n        '\\|\\| CMDLINE \\>\\>.*\\<\\< CMDLINE \\|\\|',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # clean cmdline in-line bits\n    docs = re.sub(\n        '\\[CMD:\\s[^\\[\\]]*\\sCMD\\]',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    docs = re.sub(\n        '\\[PY:\\s([^\\[\\]]*)\\sPY\\]',\n        lambda match: match.group(1),\n        docs,\n        flags=re.MULTILINE)\n    docs = re.sub(\n        '\\|\\| PYTHON \\>\\>(.*)\\<\\< PYTHON \\|\\|',\n        lambda match: match.group(1),\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    docs = re.sub(\n        '\\|\\| REFLOW \\>\\>\\n(.*)\\<\\< REFLOW \\|\\|',\n        lambda match: textwrap.fill(match.group(1)),\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    return docs",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 93,
                            "end_lineno": 127,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 115,
                            "length": 12
                        },
                        "new_range": {
                            "start": 115,
                            "length": 12
                        },
                        "section": "def alter_interface_docs_for_api(docs):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        docs,",
                                "type": "context"
                            },
                            {
                                "content": "        flags=re.MULTILINE)",
                                "type": "context"
                            },
                            {
                                "content": "    docs = re.sub(",
                                "type": "context"
                            },
                            {
                                "content": "        '\\|\\| PYTHON \\>\\>(.*)\\<\\< PYTHON \\|\\|',",
                                "type": "deleted"
                            },
                            {
                                "content": "        '\\|\\| PYTHON \\>\\>(.*?)\\<\\< PYTHON \\|\\|',",
                                "type": "added"
                            },
                            {
                                "content": "        lambda match: match.group(1),",
                                "type": "context"
                            },
                            {
                                "content": "        docs,",
                                "type": "context"
                            },
                            {
                                "content": "        flags=re.MULTILINE | re.DOTALL)",
                                "type": "context"
                            },
                            {
                                "content": "    docs = re.sub(",
                                "type": "context"
                            },
                            {
                                "content": "        '\\|\\| REFLOW \\>\\>\\n(.*)\\<\\< REFLOW \\|\\|',",
                                "type": "deleted"
                            },
                            {
                                "content": "        '\\|\\| REFLOW \\>\\>\\n(.*?)\\<\\< REFLOW \\|\\|',",
                                "type": "added"
                            },
                            {
                                "content": "        lambda match: textwrap.fill(match.group(1)),",
                                "type": "context"
                            },
                            {
                                "content": "        docs,",
                                "type": "context"
                            },
                            {
                                "content": "        flags=re.MULTILINE | re.DOTALL)",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "datalad/interface/base.py",
                            "type": "function",
                            "name": "alter_interface_docs_for_api",
                            "content": "def alter_interface_docs_for_api(docs):\n    \"\"\"Apply modifications to interface docstrings for Python API use.\"\"\"\n    # central place to alter the impression of docstrings,\n    # like removing cmdline specific sections\n    if not docs:\n        return docs\n    docs = dedent_docstring(docs)\n    # clean cmdline sections\n    docs = re.sub(\n        '\\|\\| CMDLINE \\>\\>.*\\<\\< CMDLINE \\|\\|',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # clean cmdline in-line bits\n    docs = re.sub(\n        '\\[CMD:\\s[^\\[\\]]*\\sCMD\\]',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    docs = re.sub(\n        '\\[PY:\\s([^\\[\\]]*)\\sPY\\]',\n        lambda match: match.group(1),\n        docs,\n        flags=re.MULTILINE)\n    docs = re.sub(\n        '\\|\\| PYTHON \\>\\>(.*)\\<\\< PYTHON \\|\\|',\n        lambda match: match.group(1),\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    docs = re.sub(\n        '\\|\\| REFLOW \\>\\>\\n(.*)\\<\\< REFLOW \\|\\|',\n        lambda match: textwrap.fill(match.group(1)),\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    return docs",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 93,
                            "end_lineno": 127,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 136,
                            "length": 7
                        },
                        "new_range": {
                            "start": 136,
                            "length": 7
                        },
                        "section": "def alter_interface_docs_for_cmdline(docs):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    docs = dedent_docstring(docs)",
                                "type": "context"
                            },
                            {
                                "content": "    # clean cmdline sections",
                                "type": "context"
                            },
                            {
                                "content": "    docs = re.sub(",
                                "type": "context"
                            },
                            {
                                "content": "        '\\|\\| PYTHON \\>\\>.*\\<\\< PYTHON \\|\\|',",
                                "type": "deleted"
                            },
                            {
                                "content": "        '\\|\\| PYTHON \\>\\>.*?\\<\\< PYTHON \\|\\|',",
                                "type": "added"
                            },
                            {
                                "content": "        '',",
                                "type": "context"
                            },
                            {
                                "content": "        docs,",
                                "type": "context"
                            },
                            {
                                "content": "        flags=re.MULTILINE | re.DOTALL)",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "datalad/interface/base.py",
                            "type": "function",
                            "name": "alter_interface_docs_for_cmdline",
                            "content": "def alter_interface_docs_for_cmdline(docs):\n    \"\"\"Apply modifications to interface docstrings for cmdline doc use.\"\"\"\n    # central place to alter the impression of docstrings,\n    # like removing Python API specific sections, and argument markup\n    if not docs:\n        return docs\n    docs = dedent_docstring(docs)\n    # clean cmdline sections\n    docs = re.sub(\n        '\\|\\| PYTHON \\>\\>.*\\<\\< PYTHON \\|\\|',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # clean cmdline in-line bits\n    docs = re.sub(\n        '\\[PY:\\s[^\\[\\]]*\\sPY\\]',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    docs = re.sub(\n        '\\[CMD:\\s([^\\[\\]]*)\\sCMD\\]',\n        lambda match: match.group(1),\n        docs,\n        flags=re.MULTILINE)\n    docs = re.sub(\n        '\\|\\| CMDLINE \\>\\>(.*)\\<\\< CMDLINE \\|\\|',\n        lambda match: match.group(1),\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # remove :role:`...` RST markup for cmdline docs\n    docs = re.sub(\n        r':\\S+:`[^`]*`[\\\\]*',\n        lambda match: ':'.join(match.group(0).split(':')[2:]).strip('`\\\\'),\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # remove None constraint. In general, `None` on the cmdline means don't\n    # give option at all, but specifying `None` explicitly is practically\n    # impossible\n    docs = re.sub(\n        ',\\sor\\svalue\\smust\\sbe\\s`None`',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # capitalize variables and remove backticks to uniformize with\n    # argparse output\n    docs = re.sub(\n        '`\\S*`',\n        lambda match: match.group(0).strip('`').upper(),\n        docs)\n    # clean up sphinx API refs\n    docs = re.sub(\n        '\\~datalad\\.api\\.\\S*',\n        lambda match: \"`{0}`\".format(match.group(0)[13:]),\n        docs)\n    # Remove RST paragraph markup\n    docs = re.sub(\n        r'^.. \\S+::',\n        lambda match: match.group(0)[3:-2].upper(),\n        docs,\n        flags=re.MULTILINE)\n    docs = re.sub(\n        '\\|\\| REFLOW \\>\\>\\n(.*)\\<\\< REFLOW \\|\\|',\n        lambda match: textwrap.fill(match.group(1)),\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    return docs",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 130,
                            "end_lineno": 195,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 152,
                            "length": 7
                        },
                        "new_range": {
                            "start": 152,
                            "length": 7
                        },
                        "section": "def alter_interface_docs_for_cmdline(docs):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        docs,",
                                "type": "context"
                            },
                            {
                                "content": "        flags=re.MULTILINE)",
                                "type": "context"
                            },
                            {
                                "content": "    docs = re.sub(",
                                "type": "context"
                            },
                            {
                                "content": "        '\\|\\| CMDLINE \\>\\>(.*)\\<\\< CMDLINE \\|\\|',",
                                "type": "deleted"
                            },
                            {
                                "content": "        '\\|\\| CMDLINE \\>\\>(.*?)\\<\\< CMDLINE \\|\\|',",
                                "type": "added"
                            },
                            {
                                "content": "        lambda match: match.group(1),",
                                "type": "context"
                            },
                            {
                                "content": "        docs,",
                                "type": "context"
                            },
                            {
                                "content": "        flags=re.MULTILINE | re.DOTALL)",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "datalad/interface/base.py",
                            "type": "function",
                            "name": "alter_interface_docs_for_cmdline",
                            "content": "def alter_interface_docs_for_cmdline(docs):\n    \"\"\"Apply modifications to interface docstrings for cmdline doc use.\"\"\"\n    # central place to alter the impression of docstrings,\n    # like removing Python API specific sections, and argument markup\n    if not docs:\n        return docs\n    docs = dedent_docstring(docs)\n    # clean cmdline sections\n    docs = re.sub(\n        '\\|\\| PYTHON \\>\\>.*\\<\\< PYTHON \\|\\|',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # clean cmdline in-line bits\n    docs = re.sub(\n        '\\[PY:\\s[^\\[\\]]*\\sPY\\]',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    docs = re.sub(\n        '\\[CMD:\\s([^\\[\\]]*)\\sCMD\\]',\n        lambda match: match.group(1),\n        docs,\n        flags=re.MULTILINE)\n    docs = re.sub(\n        '\\|\\| CMDLINE \\>\\>(.*)\\<\\< CMDLINE \\|\\|',\n        lambda match: match.group(1),\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # remove :role:`...` RST markup for cmdline docs\n    docs = re.sub(\n        r':\\S+:`[^`]*`[\\\\]*',\n        lambda match: ':'.join(match.group(0).split(':')[2:]).strip('`\\\\'),\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # remove None constraint. In general, `None` on the cmdline means don't\n    # give option at all, but specifying `None` explicitly is practically\n    # impossible\n    docs = re.sub(\n        ',\\sor\\svalue\\smust\\sbe\\s`None`',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # capitalize variables and remove backticks to uniformize with\n    # argparse output\n    docs = re.sub(\n        '`\\S*`',\n        lambda match: match.group(0).strip('`').upper(),\n        docs)\n    # clean up sphinx API refs\n    docs = re.sub(\n        '\\~datalad\\.api\\.\\S*',\n        lambda match: \"`{0}`\".format(match.group(0)[13:]),\n        docs)\n    # Remove RST paragraph markup\n    docs = re.sub(\n        r'^.. \\S+::',\n        lambda match: match.group(0)[3:-2].upper(),\n        docs,\n        flags=re.MULTILINE)\n    docs = re.sub(\n        '\\|\\| REFLOW \\>\\>\\n(.*)\\<\\< REFLOW \\|\\|',\n        lambda match: textwrap.fill(match.group(1)),\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    return docs",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 130,
                            "end_lineno": 195,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 188,
                            "length": 7
                        },
                        "new_range": {
                            "start": 188,
                            "length": 7
                        },
                        "section": "def alter_interface_docs_for_cmdline(docs):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        docs,",
                                "type": "context"
                            },
                            {
                                "content": "        flags=re.MULTILINE)",
                                "type": "context"
                            },
                            {
                                "content": "    docs = re.sub(",
                                "type": "context"
                            },
                            {
                                "content": "        '\\|\\| REFLOW \\>\\>\\n(.*)\\<\\< REFLOW \\|\\|',",
                                "type": "deleted"
                            },
                            {
                                "content": "        '\\|\\| REFLOW \\>\\>\\n(.*?)\\<\\< REFLOW \\|\\|',",
                                "type": "added"
                            },
                            {
                                "content": "        lambda match: textwrap.fill(match.group(1)),",
                                "type": "context"
                            },
                            {
                                "content": "        docs,",
                                "type": "context"
                            },
                            {
                                "content": "        flags=re.MULTILINE | re.DOTALL)",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "datalad/interface/base.py",
                            "type": "function",
                            "name": "alter_interface_docs_for_cmdline",
                            "content": "def alter_interface_docs_for_cmdline(docs):\n    \"\"\"Apply modifications to interface docstrings for cmdline doc use.\"\"\"\n    # central place to alter the impression of docstrings,\n    # like removing Python API specific sections, and argument markup\n    if not docs:\n        return docs\n    docs = dedent_docstring(docs)\n    # clean cmdline sections\n    docs = re.sub(\n        '\\|\\| PYTHON \\>\\>.*\\<\\< PYTHON \\|\\|',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # clean cmdline in-line bits\n    docs = re.sub(\n        '\\[PY:\\s[^\\[\\]]*\\sPY\\]',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    docs = re.sub(\n        '\\[CMD:\\s([^\\[\\]]*)\\sCMD\\]',\n        lambda match: match.group(1),\n        docs,\n        flags=re.MULTILINE)\n    docs = re.sub(\n        '\\|\\| CMDLINE \\>\\>(.*)\\<\\< CMDLINE \\|\\|',\n        lambda match: match.group(1),\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # remove :role:`...` RST markup for cmdline docs\n    docs = re.sub(\n        r':\\S+:`[^`]*`[\\\\]*',\n        lambda match: ':'.join(match.group(0).split(':')[2:]).strip('`\\\\'),\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # remove None constraint. In general, `None` on the cmdline means don't\n    # give option at all, but specifying `None` explicitly is practically\n    # impossible\n    docs = re.sub(\n        ',\\sor\\svalue\\smust\\sbe\\s`None`',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # capitalize variables and remove backticks to uniformize with\n    # argparse output\n    docs = re.sub(\n        '`\\S*`',\n        lambda match: match.group(0).strip('`').upper(),\n        docs)\n    # clean up sphinx API refs\n    docs = re.sub(\n        '\\~datalad\\.api\\.\\S*',\n        lambda match: \"`{0}`\".format(match.group(0)[13:]),\n        docs)\n    # Remove RST paragraph markup\n    docs = re.sub(\n        r'^.. \\S+::',\n        lambda match: match.group(0)[3:-2].upper(),\n        docs,\n        flags=re.MULTILINE)\n    docs = re.sub(\n        '\\|\\| REFLOW \\>\\>\\n(.*)\\<\\< REFLOW \\|\\|',\n        lambda match: textwrap.fill(match.group(1)),\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    return docs",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 130,
                            "end_lineno": 195,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-\n# ex: set sts=4 ts=4 sw=4 noet:\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n#\n#   See COPYING file distributed along with the datalad package for the\n#   copyright and license terms.\n#\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n\"\"\"Tests for interface doc wranglers.\n\n\"\"\"\n\n__docformat__ = 'restructuredtext'\n\nfrom datalad.interface.base import dedent_docstring\nfrom datalad.interface.base import alter_interface_docs_for_api\nfrom datalad.interface.base import alter_interface_docs_for_cmdline\nfrom datalad.tests.utils import assert_true, assert_false, assert_in, \\\n    assert_not_in, eq_\n\n\ndemo_doc = \"\"\"\\\n    Bla bla summary\n\n    Generic intro blurb. Ping pong ping pong ping pong ping pong.  Ping pong ping\n    pong ping pong ping pong. Ping pong ping pong ping pong ping pong. Ping pong\n    ping pong ping pong ping pong. Ping pong ping pong ping pong ping pong. Ping\n    pong ping pong ping pong ping pong.\n\n    || CMDLINE >>\n    || REFLOW >>\n    Something for the cmdline only\n    Multiline!\n    << REFLOW ||\n    << CMDLINE ||\n\n    || PYTHON >>\n\n    || REFLOW >>\n    Some Python-only bits\n    Multiline!\n    << REFLOW ||\n\n    << PYTHON ||\n\n    And an example for in-line markup: [PY: just for Python PY] and\n    the other one [CMD: just for the command line CMD]. End of demo.\n\n    Generic appendix. Ding dong ding dong ding dong.  Ding dong ding dong ding\n    dong.  Ding dong ding dong ding dong.  Ding dong ding dong ding dong.  Ding\n    dong ding dong ding dong.\n\n\"\"\"\n\ndemo_paramdoc = \"\"\"\\\n\n    Parameters\n    ----------\n    dataset : Dataset or None, optional\n      something [PY: python only PY] inbetween [CMD: cmdline only CMD] appended [PY: more python PY]\n      dataset is given, an attempt is made to identify the dataset based\n      Dataset (e.g. a path), or value must be `None`. [Default: None]\n\"\"\"\n\ndemo_argdoc = \"\"\"\\\n    specify the dataset to perform the install operation\n    on. If no dataset is given, an attempt is made to\n    identify the dataset based on the current working\n    directory and/or the `path` given. Constraints: Value\n    must be a Dataset or a valid identifier of a Dataset\n    (e.g. a path), or value must be `None`. [Default:\n    None]\n\"\"\"\n\n\ndef test_dedent():\n    assert_false(dedent_docstring(\"one liner\").endswith(\"\\n\"))\n\n\ndef test_alter_interface_docs_for_api():\n    alt = alter_interface_docs_for_api(demo_doc)\n    alt_l = alt.split('\\n')\n    # dedented\n    assert_false(alt_l[0].startswith(' '))\n    assert_false(alt_l[-1].startswith(' '))\n    assert_not_in('CMD', alt)\n    assert_not_in('PY', alt)\n    assert_not_in('REFLOW', alt)\n    assert_in(\"Some Python-only bits Multiline!\", alt)\n\n    altpd = alter_interface_docs_for_api(demo_paramdoc)\n    assert_in('python', altpd)\n    assert_in('inbetween', altpd)\n    assert_in('appended', altpd)\n    assert_not_in('cmdline', altpd)\n\n\ndef test_alter_interface_docs_for_cmdline():\n    alt = alter_interface_docs_for_cmdline(demo_doc)\n    alt_l = alt.split('\\n')\n    # dedented\n    assert_false(alt_l[0].startswith(' '))\n    assert_false(alt_l[-1].startswith(' '))\n    assert_not_in('PY', alt)\n    assert_not_in('CMD', alt)\n    assert_not_in('REFLOW', alt)\n    assert_in(\"Something for the cmdline only Multiline!\", alt)\n    # args\n    altarg = alter_interface_docs_for_cmdline(demo_argdoc)\n    # RST role markup\n    eq_(alter_interface_docs_for_cmdline(':murks:`me and my buddies`'),\n        'me and my buddies')\n    # spread across lines\n    eq_(alter_interface_docs_for_cmdline(':term:`Barbara\\nStreisand`'),\n        'Barbara\\nStreisand')\n    # multiple on one line\n    eq_(alter_interface_docs_for_cmdline(\n        ':term:`one` bla bla :term:`two` bla'),\n        'one bla bla two bla')\n\n    altpd = alter_interface_docs_for_cmdline(demo_paramdoc)\n    assert_not_in('python', altpd)\n    assert_in('inbetween', altpd)\n    assert_in('appended', altpd)\n    assert_in('cmdline', altpd)\n",
            "new_file_content": "# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-\n# ex: set sts=4 ts=4 sw=4 noet:\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n#\n#   See COPYING file distributed along with the datalad package for the\n#   copyright and license terms.\n#\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n\"\"\"Tests for interface doc wranglers.\n\n\"\"\"\n\n__docformat__ = 'restructuredtext'\n\nfrom datalad.interface.base import dedent_docstring\nfrom datalad.interface.base import alter_interface_docs_for_api\nfrom datalad.interface.base import alter_interface_docs_for_cmdline\nfrom datalad.tests.utils import assert_true, assert_false, assert_in, \\\n    assert_not_in, eq_\n\n\ndemo_doc = \"\"\"\\\n    Bla bla summary\n\n    Generic intro blurb. Ping pong ping pong ping pong ping pong.  Ping pong ping\n    pong ping pong ping pong. Ping pong ping pong ping pong ping pong. Ping pong\n    ping pong ping pong ping pong. Ping pong ping pong ping pong ping pong. Ping\n    pong ping pong ping pong ping pong.\n\n    || CMDLINE >>\n    || REFLOW >>\n    Something for the cmdline only\n    Multiline!\n    << REFLOW ||\n    << CMDLINE ||\n\n    || REFLOW >>\n    a\n    b\n    << REFLOW ||\n    not\n       reflowed\n    || REFLOW >>\n    c\n    << REFLOW ||\n\n    || PYTHON >>\n\n    || REFLOW >>\n    Some Python-only bits\n    Multiline!\n    << REFLOW ||\n\n    << PYTHON ||\n\n    And an example for in-line markup: [PY: just for Python PY] and\n    the other one [CMD: just for the command line CMD]. End of demo.\n\n    Generic appendix. Ding dong ding dong ding dong.  Ding dong ding dong ding\n    dong.  Ding dong ding dong ding dong.  Ding dong ding dong ding dong.  Ding\n    dong ding dong ding dong.\n\n\"\"\"\n\ndemo_paramdoc = \"\"\"\\\n\n    Parameters\n    ----------\n    dataset : Dataset or None, optional\n      something [PY: python only PY] inbetween [CMD: cmdline only CMD] appended [PY: more python PY]\n      dataset is given, an attempt is made to identify the dataset based\n      Dataset (e.g. a path), or value must be `None`. [Default: None]\n\"\"\"\n\ndemo_argdoc = \"\"\"\\\n    specify the dataset to perform the install operation\n    on. If no dataset is given, an attempt is made to\n    identify the dataset based on the current working\n    directory and/or the `path` given. Constraints: Value\n    must be a Dataset or a valid identifier of a Dataset\n    (e.g. a path), or value must be `None`. [Default:\n    None]\n\"\"\"\n\n\ndef test_dedent():\n    assert_false(dedent_docstring(\"one liner\").endswith(\"\\n\"))\n\n\ndef test_alter_interface_docs_for_api():\n    alt = alter_interface_docs_for_api(demo_doc)\n    alt_l = alt.split('\\n')\n    # dedented\n    assert_false(alt_l[0].startswith(' '))\n    assert_false(alt_l[-1].startswith(' '))\n    assert_not_in('CMD', alt)\n    assert_not_in('PY', alt)\n    assert_not_in('REFLOW', alt)\n    assert_in('a b', alt)\n    assert_in('not\\n   reflowed', alt)\n    assert_in(\"Some Python-only bits Multiline!\", alt)\n\n    altpd = alter_interface_docs_for_api(demo_paramdoc)\n    assert_in('python', altpd)\n    assert_in('inbetween', altpd)\n    assert_in('appended', altpd)\n    assert_not_in('cmdline', altpd)\n\n\ndef test_alter_interface_docs_for_cmdline():\n    alt = alter_interface_docs_for_cmdline(demo_doc)\n    alt_l = alt.split('\\n')\n    # dedented\n    assert_false(alt_l[0].startswith(' '))\n    assert_false(alt_l[-1].startswith(' '))\n    assert_not_in('PY', alt)\n    assert_not_in('CMD', alt)\n    assert_not_in('REFLOW', alt)\n    assert_in('a b', alt)\n    assert_in('not\\n   reflowed', alt)\n    assert_in(\"Something for the cmdline only Multiline!\", alt)\n    # args\n    altarg = alter_interface_docs_for_cmdline(demo_argdoc)\n    # RST role markup\n    eq_(alter_interface_docs_for_cmdline(':murks:`me and my buddies`'),\n        'me and my buddies')\n    # spread across lines\n    eq_(alter_interface_docs_for_cmdline(':term:`Barbara\\nStreisand`'),\n        'Barbara\\nStreisand')\n    # multiple on one line\n    eq_(alter_interface_docs_for_cmdline(\n        ':term:`one` bla bla :term:`two` bla'),\n        'one bla bla two bla')\n\n    altpd = alter_interface_docs_for_cmdline(demo_paramdoc)\n    assert_not_in('python', altpd)\n    assert_in('inbetween', altpd)\n    assert_in('appended', altpd)\n    assert_in('cmdline', altpd)\n",
            "header": {
                "file": {
                    "path": "datalad/interface/tests/test_docs.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "5646b1aa0",
                "new_commit_hash": "dbcd139f3",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/datalad/interface/tests/test_docs.py"
            },
            "plus_file": {
                "path": "b/datalad/interface/tests/test_docs.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 34,
                            "length": 6
                        },
                        "new_range": {
                            "start": 34,
                            "length": 16
                        },
                        "section": "demo_doc = \"\"\"\\"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    << REFLOW ||",
                                "type": "context"
                            },
                            {
                                "content": "    << CMDLINE ||",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    || REFLOW >>",
                                "type": "added"
                            },
                            {
                                "content": "    a",
                                "type": "added"
                            },
                            {
                                "content": "    b",
                                "type": "added"
                            },
                            {
                                "content": "    << REFLOW ||",
                                "type": "added"
                            },
                            {
                                "content": "    not",
                                "type": "added"
                            },
                            {
                                "content": "       reflowed",
                                "type": "added"
                            },
                            {
                                "content": "    || REFLOW >>",
                                "type": "added"
                            },
                            {
                                "content": "    c",
                                "type": "added"
                            },
                            {
                                "content": "    << REFLOW ||",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    || PYTHON >>",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    || REFLOW >>",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [
                        {
                            "file_name": "datalad/interface/tests/test_docs.py",
                            "type": "statement",
                            "name": "",
                            "content": "demo_doc = \"\"\"\\\n    Bla bla summary\n\n    Generic intro blurb. Ping pong ping pong ping pong ping pong.  Ping pong ping\n    pong ping pong ping pong. Ping pong ping pong ping pong ping pong. Ping pong\n    ping pong ping pong ping pong. Ping pong ping pong ping pong ping pong. Ping\n    pong ping pong ping pong ping pong.\n\n    || CMDLINE >>\n    || REFLOW >>\n    Something for the cmdline only\n    Multiline!\n    << REFLOW ||\n    << CMDLINE ||\n\n    || REFLOW >>\n    a\n    b\n    << REFLOW ||\n    not\n       reflowed\n    || REFLOW >>\n    c\n    << REFLOW ||\n\n    || PYTHON >>\n\n    || REFLOW >>\n    Some Python-only bits\n    Multiline!\n    << REFLOW ||\n\n    << PYTHON ||\n\n    And an example for in-line markup: [PY: just for Python PY] and\n    the other one [CMD: just for the command line CMD]. End of demo.\n\n    Generic appendix. Ding dong ding dong ding dong.  Ding dong ding dong ding\n    dong.  Ding dong ding dong ding dong.  Ding dong ding dong ding dong.  Ding\n    dong ding dong ding dong.\n\n\"\"\"",
                            "ast_type_str": "Assign",
                            "start_lineno": 22,
                            "end_lineno": 63,
                            "parent": null
                        }
                    ],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 86,
                            "length": 6
                        },
                        "new_range": {
                            "start": 96,
                            "length": 8
                        },
                        "section": "def test_alter_interface_docs_for_api():"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    assert_not_in('CMD', alt)",
                                "type": "context"
                            },
                            {
                                "content": "    assert_not_in('PY', alt)",
                                "type": "context"
                            },
                            {
                                "content": "    assert_not_in('REFLOW', alt)",
                                "type": "context"
                            },
                            {
                                "content": "    assert_in('a b', alt)",
                                "type": "added"
                            },
                            {
                                "content": "    assert_in('not\\n   reflowed', alt)",
                                "type": "added"
                            },
                            {
                                "content": "    assert_in(\"Some Python-only bits Multiline!\", alt)",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    altpd = alter_interface_docs_for_api(demo_paramdoc)",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "datalad/interface/tests/test_docs.py",
                            "type": "function",
                            "name": "test_alter_interface_docs_for_api",
                            "content": "def test_alter_interface_docs_for_api():\n    alt = alter_interface_docs_for_api(demo_doc)\n    alt_l = alt.split('\\n')\n    # dedented\n    assert_false(alt_l[0].startswith(' '))\n    assert_false(alt_l[-1].startswith(' '))\n    assert_not_in('CMD', alt)\n    assert_not_in('PY', alt)\n    assert_not_in('REFLOW', alt)\n    assert_in('a b', alt)\n    assert_in('not\\n   reflowed', alt)\n    assert_in(\"Some Python-only bits Multiline!\", alt)\n\n    altpd = alter_interface_docs_for_api(demo_paramdoc)\n    assert_in('python', altpd)\n    assert_in('inbetween', altpd)\n    assert_in('appended', altpd)\n    assert_not_in('cmdline', altpd)",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 90,
                            "end_lineno": 107,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 104,
                            "length": 6
                        },
                        "new_range": {
                            "start": 116,
                            "length": 8
                        },
                        "section": "def test_alter_interface_docs_for_cmdline():"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    assert_not_in('PY', alt)",
                                "type": "context"
                            },
                            {
                                "content": "    assert_not_in('CMD', alt)",
                                "type": "context"
                            },
                            {
                                "content": "    assert_not_in('REFLOW', alt)",
                                "type": "context"
                            },
                            {
                                "content": "    assert_in('a b', alt)",
                                "type": "added"
                            },
                            {
                                "content": "    assert_in('not\\n   reflowed', alt)",
                                "type": "added"
                            },
                            {
                                "content": "    assert_in(\"Something for the cmdline only Multiline!\", alt)",
                                "type": "context"
                            },
                            {
                                "content": "    # args",
                                "type": "context"
                            },
                            {
                                "content": "    altarg = alter_interface_docs_for_cmdline(demo_argdoc)",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "datalad/interface/tests/test_docs.py",
                            "type": "function",
                            "name": "test_alter_interface_docs_for_cmdline",
                            "content": "def test_alter_interface_docs_for_cmdline():\n    alt = alter_interface_docs_for_cmdline(demo_doc)\n    alt_l = alt.split('\\n')\n    # dedented\n    assert_false(alt_l[0].startswith(' '))\n    assert_false(alt_l[-1].startswith(' '))\n    assert_not_in('PY', alt)\n    assert_not_in('CMD', alt)\n    assert_not_in('REFLOW', alt)\n    assert_in('a b', alt)\n    assert_in('not\\n   reflowed', alt)\n    assert_in(\"Something for the cmdline only Multiline!\", alt)\n    # args\n    altarg = alter_interface_docs_for_cmdline(demo_argdoc)\n    # RST role markup\n    eq_(alter_interface_docs_for_cmdline(':murks:`me and my buddies`'),\n        'me and my buddies')\n    # spread across lines\n    eq_(alter_interface_docs_for_cmdline(':term:`Barbara\\nStreisand`'),\n        'Barbara\\nStreisand')\n    # multiple on one line\n    eq_(alter_interface_docs_for_cmdline(\n        ':term:`one` bla bla :term:`two` bla'),\n        'one bla bla two bla')\n\n    altpd = alter_interface_docs_for_cmdline(demo_paramdoc)\n    assert_not_in('python', altpd)\n    assert_in('inbetween', altpd)\n    assert_in('appended', altpd)\n    assert_in('cmdline', altpd)",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 110,
                            "end_lineno": 139,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "cd3ead491e4a63a50efd1a4737954077da2a7c2d^",
    "new_commit_hash": "cd3ead491e4a63a50efd1a4737954077da2a7c2d",
    "commit_message": "BF: interface: Don't be greedy when scanning for closing doc tags\n\nThese should stop matching once the closing tag has been reached.\nOtherwise it's not possible to write something like\n\n    || REFLOW >>\n    reflowed paragraph\n    << REFLOW ||\n\n      - a\n      - b\n\n    || REFLOW >>\n    another reflowed paragraph\n    << REFLOW ||",
    "commit_date": "2018-05-28T14:42:29-04:00",
    "metadata": {}
}
{
    "repo_name": "datalad",
    "new_commit_hash": "cd3ead491e4a63a50efd1a4737954077da2a7c2d",
    "test_file_codes": [
        "# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-\n# ex: set sts=4 ts=4 sw=4 noet:\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n#\n#   See COPYING file distributed along with the datalad package for the\n#   copyright and license terms.\n#\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n\"\"\"Tests for interface doc wranglers.\n\n\"\"\"\n\n__docformat__ = 'restructuredtext'\n\nfrom datalad.interface.base import dedent_docstring\nfrom datalad.interface.base import alter_interface_docs_for_api\nfrom datalad.interface.base import alter_interface_docs_for_cmdline\nfrom datalad.tests.utils import assert_true, assert_false, assert_in, \\\n    assert_not_in, eq_\n\n\ndemo_doc = \"\"\"\\\n    Bla bla summary\n\n    Generic intro blurb. Ping pong ping pong ping pong ping pong.  Ping pong ping\n    pong ping pong ping pong. Ping pong ping pong ping pong ping pong. Ping pong\n    ping pong ping pong ping pong. Ping pong ping pong ping pong ping pong. Ping\n    pong ping pong ping pong ping pong.\n\n    || CMDLINE >>\n    || REFLOW >>\n    Something for the cmdline only\n    Multiline!\n    << REFLOW ||\n    << CMDLINE ||\n\n    || REFLOW >>\n    a\n    b\n    << REFLOW ||\n    not\n       reflowed\n    || REFLOW >>\n    c\n    << REFLOW ||\n\n    || PYTHON >>\n\n    || REFLOW >>\n    Some Python-only bits\n    Multiline!\n    << REFLOW ||\n\n    << PYTHON ||\n\n    And an example for in-line markup: [PY: just for Python PY] and\n    the other one [CMD: just for the command line CMD]. End of demo.\n\n    Generic appendix. Ding dong ding dong ding dong.  Ding dong ding dong ding\n    dong.  Ding dong ding dong ding dong.  Ding dong ding dong ding dong.  Ding\n    dong ding dong ding dong.\n\n\"\"\"\n\ndemo_paramdoc = \"\"\"\\\n\n    Parameters\n    ----------\n    dataset : Dataset or None, optional\n      something [PY: python only PY] inbetween [CMD: cmdline only CMD] appended [PY: more python PY]\n      dataset is given, an attempt is made to identify the dataset based\n      Dataset (e.g. a path), or value must be `None`. [Default: None]\n\"\"\"\n\ndemo_argdoc = \"\"\"\\\n    specify the dataset to perform the install operation\n    on. If no dataset is given, an attempt is made to\n    identify the dataset based on the current working\n    directory and/or the `path` given. Constraints: Value\n    must be a Dataset or a valid identifier of a Dataset\n    (e.g. a path), or value must be `None`. [Default:\n    None]\n\"\"\"\n\n\ndef test_dedent():\n    assert_false(dedent_docstring(\"one liner\").endswith(\"\\n\"))\n\n\ndef test_alter_interface_docs_for_api():\n    alt = alter_interface_docs_for_api(demo_doc)\n    alt_l = alt.split('\\n')\n    # dedented\n    assert_false(alt_l[0].startswith(' '))\n    assert_false(alt_l[-1].startswith(' '))\n    assert_not_in('CMD', alt)\n    assert_not_in('PY', alt)\n    assert_not_in('REFLOW', alt)\n    assert_in('a b', alt)\n    assert_in('not\\n   reflowed', alt)\n    assert_in(\"Some Python-only bits Multiline!\", alt)\n\n    altpd = alter_interface_docs_for_api(demo_paramdoc)\n    assert_in('python', altpd)\n    assert_in('inbetween', altpd)\n    assert_in('appended', altpd)\n    assert_not_in('cmdline', altpd)\n\n\ndef test_alter_interface_docs_for_cmdline():\n    alt = alter_interface_docs_for_cmdline(demo_doc)\n    alt_l = alt.split('\\n')\n    # dedented\n    assert_false(alt_l[0].startswith(' '))\n    assert_false(alt_l[-1].startswith(' '))\n    assert_not_in('PY', alt)\n    assert_not_in('CMD', alt)\n    assert_not_in('REFLOW', alt)\n    assert_in('a b', alt)\n    assert_in('not\\n   reflowed', alt)\n    assert_in(\"Something for the cmdline only Multiline!\", alt)\n    # args\n    altarg = alter_interface_docs_for_cmdline(demo_argdoc)\n    # RST role markup\n    eq_(alter_interface_docs_for_cmdline(':murks:`me and my buddies`'),\n        'me and my buddies')\n    # spread across lines\n    eq_(alter_interface_docs_for_cmdline(':term:`Barbara\\nStreisand`'),\n        'Barbara\\nStreisand')\n    # multiple on one line\n    eq_(alter_interface_docs_for_cmdline(\n        ':term:`one` bla bla :term:`two` bla'),\n        'one bla bla two bla')\n\n    altpd = alter_interface_docs_for_cmdline(demo_paramdoc)\n    assert_not_in('python', altpd)\n    assert_in('inbetween', altpd)\n    assert_in('appended', altpd)\n    assert_in('cmdline', altpd)\n",
        "import pytest\nimport tempfile\nimport os\n\n\n@pytest.fixture\ndef path():\n    with tempfile.TemporaryDirectory() as tmpdir:\n        yield tmpdir\n\n\n@pytest.fixture(autouse=True)\ndef setup_git_config():\n    with tempfile.TemporaryDirectory() as home:\n        old_home = os.environ.get(\"HOME\")\n        os.environ[\"HOME\"] = home\n        os.system('git config --global user.name \"DataLad Tester\"')\n        os.system('git config --global user.email \"test@example.com\"')\n        yield\n        if old_home:\n            os.environ[\"HOME\"] = old_home\n"
    ],
    "test_file_names": [
        "test_1.py",
        "conftest.py"
    ],
    "setup_res_code": 0,
    "setup_res_stdout": "Starting Datalad installation attempts...\nVerifying installation...\n❌ Verification failed\nPython 3.9 installation failed, trying Python 3.7...\nVerifying installation...\nDatalad version: 0.10.0.rc4.dev146\n✅ Installation successful!\nSuccessfully installed Datalad using Python 3.7\n",
    "setup_res_stderr": "Using CPython 3.9.21\nCreating virtual environment at: .venv\nActivate with: source .venv/bin/activate\nResolved 25 packages in 68ms\nInstalled 25 packages in 4.34s\n + coverage==7.6.10\n + docopt==0.6.2\n + exceptiongroup==1.2.2\n + grabbit==0.2.6\n + importlib-resources==6.4.5\n + iniconfig==2.0.0\n + nibabel==5.3.2\n + num2words==0.5.14\n + numpy==2.0.2\n + packaging==24.2\n + pandas==2.2.3\n + patsy==1.0.1\n + pluggy==1.5.0\n + pybids==0.6.5\n + pytest==8.3.4\n + pytest-cov==6.0.0\n + python-dateutil==2.9.0.post0\n + pytz==2024.2\n + scipy==1.13.1\n + setuptools==75.6.0\n + six==1.17.0\n + tomli==2.2.1\n + typing-extensions==4.12.2\n + tzdata==2024.2\n + zipp==3.21.0\nerror: The build backend returned an error\n  Caused by: Call to `setuptools.build_meta:__legacy__.build_editable` failed (exit status: 1)\n\n[stderr]\nTraceback (most recent call last):\n  File \"<string>\", line 14, in <module>\n  File \"/home/gcpuser/.cache/uv/builds-v0/.tmpzzFHTR/lib/python3.9/site-packages/setuptools/build_meta.py\", line 483, in get_requires_for_build_editable\n    return self.get_requires_for_build_wheel(config_settings)\n  File \"/home/gcpuser/.cache/uv/builds-v0/.tmpzzFHTR/lib/python3.9/site-packages/setuptools/build_meta.py\", line 334, in get_requires_for_build_wheel\n    return self._get_build_requires(config_settings, requirements=[])\n  File \"/home/gcpuser/.cache/uv/builds-v0/.tmpzzFHTR/lib/python3.9/site-packages/setuptools/build_meta.py\", line 304, in _get_build_requires\n    self.run_setup()\n  File \"/home/gcpuser/.cache/uv/builds-v0/.tmpzzFHTR/lib/python3.9/site-packages/setuptools/build_meta.py\", line 522, in run_setup\n    super().run_setup(setup_script=setup_script)\n  File \"/home/gcpuser/.cache/uv/builds-v0/.tmpzzFHTR/lib/python3.9/site-packages/setuptools/build_meta.py\", line 320, in run_setup\n    exec(code, locals())\n  File \"<string>\", line 50, in <module>\nAttributeError: module 'platform' has no attribute 'dist'\n\nhint: This usually indicates a problem with the package or the build environment.\nerror: The build backend returned an error\n  Caused by: Call to `setuptools.build_meta:__legacy__.build_editable` failed (exit status: 1)\n\n[stderr]\nTraceback (most recent call last):\n  File \"<string>\", line 14, in <module>\n  File \"/home/gcpuser/.cache/uv/builds-v0/.tmpDdKFMT/lib/python3.9/site-packages/setuptools/build_meta.py\", line 483, in get_requires_for_build_editable\n    return self.get_requires_for_build_wheel(config_settings)\n  File \"/home/gcpuser/.cache/uv/builds-v0/.tmpDdKFMT/lib/python3.9/site-packages/setuptools/build_meta.py\", line 334, in get_requires_for_build_wheel\n    return self._get_build_requires(config_settings, requirements=[])\n  File \"/home/gcpuser/.cache/uv/builds-v0/.tmpDdKFMT/lib/python3.9/site-packages/setuptools/build_meta.py\", line 304, in _get_build_requires\n    self.run_setup()\n  File \"/home/gcpuser/.cache/uv/builds-v0/.tmpDdKFMT/lib/python3.9/site-packages/setuptools/build_meta.py\", line 522, in run_setup\n    super().run_setup(setup_script=setup_script)\n  File \"/home/gcpuser/.cache/uv/builds-v0/.tmpDdKFMT/lib/python3.9/site-packages/setuptools/build_meta.py\", line 320, in run_setup\n    exec(code, locals())\n  File \"<string>\", line 50, in <module>\nAttributeError: module 'platform' has no attribute 'dist'\n\nhint: This usually indicates a problem with the package or the build environment.\nTraceback (most recent call last):\n  File \"<string>\", line 1, in <module>\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_cd3ead491e4a63a50efd1a4737954077da2a7c2d/datalad/__init__.py\", line 23, in <module>\n    from datalad.cmd import GitRunner\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_cd3ead491e4a63a50efd1a4737954077da2a7c2d/datalad/cmd.py\", line 28, in <module>\n    from .support import path as op\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_cd3ead491e4a63a50efd1a4737954077da2a7c2d/datalad/support/path.py\", line 18, in <module>\n    from ..utils import assure_bytes\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_cd3ead491e4a63a50efd1a4737954077da2a7c2d/datalad/utils.py\", line 24, in <module>\n    import wrapt\nModuleNotFoundError: No module named 'wrapt'\nUsing CPython 3.7.9\nCreating virtual environment at: .venv\nActivate with: source .venv/bin/activate\nResolved 26 packages in 172ms\nInstalled 26 packages in 2.31s\n + bids==0.0\n + coverage==7.2.7\n + docopt==0.6.2\n + exceptiongroup==1.2.2\n + fasteners==0.19\n + grabbit==0.2.6\n + importlib-metadata==6.7.0\n + iniconfig==2.0.0\n + nibabel==4.0.2\n + num2words==0.5.14\n + numpy==1.21.6\n + packaging==24.0\n + pandas==1.3.5\n + patsy==1.0.1\n + pluggy==1.2.0\n + pybids==0.6.5\n + pytest==7.4.4\n + pytest-cov==4.1.0\n + python-dateutil==2.9.0.post0\n + pytz==2024.2\n + scipy==1.7.3\n + setuptools==68.0.0\n + six==1.17.0\n + tomli==2.0.1\n + typing-extensions==4.7.1\n + zipp==3.15.0\nResolved 60 packages in 5.87s\nPrepared 1 package in 3.04s\nInstalled 53 packages in 55ms\n + appdirs==1.4.4\n + beautifulsoup4==4.12.3\n + boto==2.49.0\n + certifi==2024.12.14\n + cffi==1.15.1\n + chardet==5.2.0\n + charset-normalizer==3.4.1\n + citeproc-py==0.6.0\n + cryptography==44.0.0\n + datalad==0.10.0rc4 (from file:///home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_cd3ead491e4a63a50efd1a4737954077da2a7c2d)\n + deprecated==1.2.15\n + duecredit==0.9.3\n + exifread==3.0.0\n + gitdb==4.0.12\n + gitpython==3.1.44\n + httpretty==1.1.4\n + humanize==4.6.0\n + idna==3.10\n + importlib-resources==5.12.0\n + iso8601==2.1.0\n + jaraco-classes==3.2.3\n + jeepney==0.8.0\n + jsmin==3.0.1\n + keyring==24.1.1\n + keyrings-alt==4.2.0\n + lxml==5.3.0\n + mock==5.1.0\n + more-itertools==9.1.0\n + msgpack==1.0.5\n + multidict==6.0.5\n + mutagen==1.47.0\n + nose==1.3.7\n + patool==1.12\n + pillow==9.5.0\n + pycparser==2.21\n + pygithub==2.3.0\n + pyjwt==2.8.0\n + pynacl==1.5.0\n + pyperclip==1.9.0\n + python-xmp-toolkit==2.0.2\n + pyyaml==6.0.1\n + requests==2.31.0\n + requests-ftp==0.3.1\n + secretstorage==3.3.3\n + simplejson==3.19.3\n + smmap==5.0.2\n + soupsieve==2.4.1\n + tqdm==4.67.1\n + urllib3==2.0.7\n + vcrpy==4.3.0\n + whoosh==2.7.4\n + wrapt==1.16.0\n + yarl==1.9.4\nResolved 92 packages in 176ms\nInstalled 30 packages in 58ms\n + alabaster==0.7.13\n + asv==0.6.4\n + asv-runner==0.2.1\n + babel==2.14.0\n + build==1.1.1\n + distlib==0.3.9\n + docutils==0.19\n + filelock==3.12.2\n + imagesize==1.4.1\n + jinja2==3.1.5\n + json5==0.9.16\n + markupsafe==2.1.5\n + nose-timer==1.0.1\n + platformdirs==4.0.0\n + pygments==2.17.2\n + pympler==1.1\n + pypandoc==1.14\n + pyproject-hooks==1.2.0\n + snowballstemmer==2.2.0\n + sphinx==5.3.0\n + sphinx-rtd-theme==2.0.0\n + sphinxcontrib-applehelp==1.0.2\n + sphinxcontrib-devhelp==1.0.2\n + sphinxcontrib-htmlhelp==2.0.0\n + sphinxcontrib-jquery==4.1\n + sphinxcontrib-jsmath==1.0.1\n + sphinxcontrib-qthelp==1.0.3\n + sphinxcontrib-serializinghtml==1.1.5\n + tabulate==0.9.0\n + virtualenv==20.26.6\n",
    "new_commit_res_code": 0,
    "new_commit_res_stdout": "============================= test session starts ==============================\nplatform linux -- Python 3.7.9, pytest-7.4.4, pluggy-1.2.0\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_cd3ead491e4a63a50efd1a4737954077da2a7c2d\nplugins: cov-4.1.0\ncollected 3 items\n\nr2e_tests/test_1.py ...                                                  [100%]\n\n==================================== PASSES ====================================\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::test_dedent\nPASSED r2e_tests/test_1.py::test_alter_interface_docs_for_api\nPASSED r2e_tests/test_1.py::test_alter_interface_docs_for_cmdline\n============================== 3 passed in 1.33s ===============================\n",
    "new_commit_res_stderr": "",
    "old_commit_res_code": 1,
    "old_commit_res_stdout": "============================= test session starts ==============================\nplatform linux -- Python 3.7.9, pytest-7.4.4, pluggy-1.2.0\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_cd3ead491e4a63a50efd1a4737954077da2a7c2d\nplugins: cov-4.1.0\ncollected 3 items\n\nr2e_tests/test_1.py .FF                                                  [100%]\n\n=================================== FAILURES ===================================\n______________________ test_alter_interface_docs_for_api _______________________\n\n    def test_alter_interface_docs_for_api():\n        alt = alter_interface_docs_for_api(demo_doc)\n        alt_l = alt.split('\\n')\n        # dedented\n        assert_false(alt_l[0].startswith(' '))\n        assert_false(alt_l[-1].startswith(' '))\n        assert_not_in('CMD', alt)\n        assert_not_in('PY', alt)\n>       assert_not_in('REFLOW', alt)\n\nr2e_tests/test_1.py:98: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n../../../../.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib/python3.7/unittest/case.py:1126: in assertNotIn\n    self.fail(self._formatMessage(msg, standardMsg))\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = <nose.tools.trivial.Dummy testMethod=nop>\nmsg = \"'REFLOW' unexpectedly found in 'Bla bla summary\\\\n\\\\nGeneric intro blurb. Ping pong ping pong ping pong ping pong.  P... ding\\\\ndong.  Ding dong ding dong ding dong.  Ding dong ding dong ding dong.  Ding\\\\ndong ding dong ding dong.\\\\n\\\\n'\"\n\n    def fail(self, msg=None):\n        \"\"\"Fail immediately, with the given message.\"\"\"\n>       raise self.failureException(msg)\nE       AssertionError: 'REFLOW' unexpectedly found in 'Bla bla summary\\n\\nGeneric intro blurb. Ping pong ping pong ping pong ping pong.  Ping pong ping\\npong ping pong ping pong. Ping pong ping pong ping pong ping pong. Ping pong\\nping pong ping pong ping pong. Ping pong ping pong ping pong ping pong. Ping\\npong ping pong ping pong ping pong.\\n\\n\\n\\na b << REFLOW || not    reflowed || REFLOW >> c << REFLOW ||    ||\\nREFLOW >> Some Python-only bits Multiline!\\n\\n\\n\\nAnd an example for in-line markup: just for Python and\\nthe other one . End of demo.\\n\\nGeneric appendix. Ding dong ding dong ding dong.  Ding dong ding dong ding\\ndong.  Ding dong ding dong ding dong.  Ding dong ding dong ding dong.  Ding\\ndong ding dong ding dong.\\n\\n'\n\n../../../../.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib/python3.7/unittest/case.py:693: AssertionError\n____________________ test_alter_interface_docs_for_cmdline _____________________\n\n    def test_alter_interface_docs_for_cmdline():\n        alt = alter_interface_docs_for_cmdline(demo_doc)\n        alt_l = alt.split('\\n')\n        # dedented\n        assert_false(alt_l[0].startswith(' '))\n        assert_false(alt_l[-1].startswith(' '))\n        assert_not_in('PY', alt)\n        assert_not_in('CMD', alt)\n>       assert_not_in('REFLOW', alt)\n\nr2e_tests/test_1.py:118: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n../../../../.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib/python3.7/unittest/case.py:1126: in assertNotIn\n    self.fail(self._formatMessage(msg, standardMsg))\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = <nose.tools.trivial.Dummy testMethod=nop>\nmsg = \"'REFLOW' unexpectedly found in 'Bla bla summary\\\\n\\\\nGeneric intro blurb. Ping pong ping pong ping pong ping pong.  P... ding\\\\ndong.  Ding dong ding dong ding dong.  Ding dong ding dong ding dong.  Ding\\\\ndong ding dong ding dong.\\\\n\\\\n'\"\n\n    def fail(self, msg=None):\n        \"\"\"Fail immediately, with the given message.\"\"\"\n>       raise self.failureException(msg)\nE       AssertionError: 'REFLOW' unexpectedly found in 'Bla bla summary\\n\\nGeneric intro blurb. Ping pong ping pong ping pong ping pong.  Ping pong ping\\npong ping pong ping pong. Ping pong ping pong ping pong ping pong. Ping pong\\nping pong ping pong ping pong. Ping pong ping pong ping pong ping pong. Ping\\npong ping pong ping pong ping pong.\\n\\n\\nSomething for the cmdline only Multiline! << REFLOW ||   || REFLOW >>\\na b << REFLOW || not    reflowed || REFLOW >> c\\n\\n\\n\\nAnd an example for in-line markup:  and\\nthe other one just for the command line. End of demo.\\n\\nGeneric appendix. Ding dong ding dong ding dong.  Ding dong ding dong ding\\ndong.  Ding dong ding dong ding dong.  Ding dong ding dong ding dong.  Ding\\ndong ding dong ding dong.\\n\\n'\n\n../../../../.local/share/uv/python/cpython-3.7.9-linux-x86_64-gnu/lib/python3.7/unittest/case.py:693: AssertionError\n==================================== PASSES ====================================\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::test_dedent\nFAILED r2e_tests/test_1.py::test_alter_interface_docs_for_api - AssertionErro...\nFAILED r2e_tests/test_1.py::test_alter_interface_docs_for_cmdline - Assertion...\n========================= 2 failed, 1 passed in 0.76s ==========================\n",
    "old_commit_res_stderr": ""
}
datalad/interface/base.py datalad/interface/tests/test_docs.py
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** Greedy Parsing of 'REFLOW' Tags Prevents Proper Documentation Formatting

**Description:**
When documentation contains multiple `REFLOW` sections, the parser fails to correctly identify the closing tags. This results in `REFLOW` tags remaining in the processed documentation, leading to formatting issues and unexpected behavior.

**Example Code:**
```python
docs = """
|| REFLOW >>
reflowed paragraph
<< REFLOW ||

  - a
  - b

|| REFLOW >>
another reflowed paragraph
<< REFLOW ||
"""

processed_docs = alter_interface_docs_for_api(docs)
print(processed_docs)
```

**Expected Behavior:**
Each `|| REFLOW >>` section should be individually processed, and all `REFLOW` tags should be removed after processing. The output should display correctly reflowed paragraphs without any remaining `REFLOW` tags.

**Actual Behavior:**
The parser does not stop at the first closing `<< REFLOW ||` tag due to greedy matching. As a result, `REFLOW` tags remain in the output, causing formatting errors in the documentation.

[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### datalad/interface/base.py
# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-
# ex: set sts=4 ts=4 sw=4 noet:
# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
#
#   See COPYING file distributed along with the datalad package for the
#   copyright and license terms.
#
# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
"""High-level interface generation

"""

__docformat__ = 'restructuredtext'

import logging
lgr = logging.getLogger('datalad.interface.base')

import sys
import re
import textwrap
import inspect
from collections import OrderedDict

from ..ui import ui
from ..dochelpers import exc_str

from datalad.interface.common_opts import eval_params
from datalad.interface.common_opts import eval_defaults
from datalad.support.constraints import EnsureKeyChoice
from datalad.distribution.dataset import Dataset
from datalad.distribution.dataset import resolve_path


default_logchannels = {
    '': 'debug',
    'ok': 'debug',
    'notneeded': 'debug',
    'impossible': 'warning',
    'error': 'error',
}


def get_api_name(intfspec):
    """Given an interface specification return an API name for it"""
    if len(intfspec) > 3:
        name = intfspec[3]
    else:
        name = intfspec[0].split('.')[-1]
    return name


def get_cmdline_command_name(intfspec):
    """Given an interface specification return a cmdline command name"""
    if len(intfspec) > 2:
        name = intfspec[2]
    else:
        name = intfspec[0].split('.')[-1].replace('_', '-')
    return name


def get_interface_groups():
    from .. import interface as _interfaces

    grps = []
    # auto detect all available interfaces and generate a function-based
    # API from them
    for _item in _interfaces.__dict__:
        if not _item.startswith('_group_'):
            continue
        grp_name = _item[7:]
        grp = getattr(_interfaces, _item)
        grps.append((grp_name,) + grp)
    return grps


def dedent_docstring(text):
    """Remove uniform indentation from a multiline docstring"""
    # Problem is that first line might often have no offset, so might
    # need to be ignored from dedent call
    if text is None:
        return None
    if not text.startswith(' '):
        lines = text.split('\n')
        if len(lines) == 1:
            # single line, no indentation, nothing to do
            return text
        text2 = '\n'.join(lines[1:])
        return lines[0] + "\n" + textwrap.dedent(text2)
    else:
        return textwrap.dedent(text)


def alter_interface_docs_for_api(docs):
    """Apply modifications to interface docstrings for Python API use."""
    # central place to alter the impression of docstrings,
    # like removing cmdline specific sections
    if not docs:
        return docs
    docs = dedent_docstring(docs)
    # clean cmdline sections
    docs = re.sub(
        '\|\| CMDLINE \>\>.*\<\< CMDLINE \|\|',
        '',
        docs,
        flags=re.MULTILINE | re.DOTALL)
    # clean cmdline in-line bits
    docs = re.sub(
        '\[CMD:\s[^\[\]]*\sCMD\]',
        '',
        docs,
        flags=re.MULTILINE | re.DOTALL)
    docs = re.sub(
        '\[PY:\s([^\[\]]*)\sPY\]',
        lambda match: match.group(1),
        docs,
        flags=re.MULTILINE)
    docs = re.sub(
        '\|\| PYTHON \>\>(.*)\<\< PYTHON \|\|',
        lambda match: match.group(1),
        docs,
        flags=re.MULTILINE | re.DOTALL)
    docs = re.sub(
        '\|\| REFLOW \>\>\n(.*)\<\< REFLOW \|\|',
        lambda match: textwrap.fill(match.group(1)),
        docs,
        flags=re.MULTILINE | re.DOTALL)
    return docs


def alter_interface_docs_for_cmdline(docs):
    """Apply modifications to interface docstrings for cmdline doc use."""
    # central place to alter the impression of docstrings,
    # like removing Python API specific sections, and argument markup
    if not docs:
        return docs
    docs = dedent_docstring(docs)
    # clean cmdline sections
    docs = re.sub(
        '\|\| PYTHON \>\>.*\<\< PYTHON \|\|',
        '',
        docs,
        flags=re.MULTILINE | re.DOTALL)
    # clean cmdline in-line bits
    docs = re.sub(
        '\[PY:\s[^\[\]]*\sPY\]',
        '',
        docs,
        flags=re.MULTILINE | re.DOTALL)
    docs = re.sub(
        '\[CMD:\s([^\[\]]*)\sCMD\]',
        lambda match: match.group(1),
        docs,
        flags=re.MULTILINE)
    docs = re.sub(
        '\|\| CMDLINE \>\>(.*)\<\< CMDLINE \|\|',
        lambda match: match.group(1),
        docs,
        flags=re.MULTILINE | re.DOTALL)
    # remove :role:`...` RST markup for cmdline docs
    docs = re.sub(
        r':\S+:`[^`]*`[\\]*',
        lambda match: ':'.join(match.group(0).split(':')[2:]).strip('`\\'),
        docs,
        flags=re.MULTILINE | re.DOTALL)
    # remove None constraint. In general, `None` on the cmdline means don't
    # give option at all, but specifying `None` explicitly is practically
    # impossible
    docs = re.sub(
        ',\sor\svalue\smust\sbe\s`None`',
        '',
        docs,
        flags=re.MULTILINE | re.DOTALL)
    # capitalize variables and remove backticks to uniformize with
    # argparse output
    docs = re.sub(
        '`\S*`',
        lambda match: match.group(0).strip('`').upper(),
        docs)
    # clean up sphinx API refs
    docs = re.sub(
        '\~datalad\.api\.\S*',
        lambda match: "`{0}`".format(match.group(0)[13:]),
        docs)
    # Remove RST paragraph markup
    docs = re.sub(
        r'^.. \S+::',
        lambda match: match.group(0)[3:-2].upper(),
        docs,
        flags=re.MULTILINE)
    docs = re.sub(
        '\|\| REFLOW \>\>\n(.*)\<\< REFLOW \|\|',
        lambda match: textwrap.fill(match.group(1)),
        docs,
        flags=re.MULTILINE | re.DOTALL)
    return docs


def is_api_arg(arg):
    """Return True if argument is our API argument or self or used for internal
    purposes
    """
    return arg != 'self' and not arg.startswith('_')


def update_docstring_with_parameters(func, params, prefix=None, suffix=None,
                                     add_args=None):
    """Generate a useful docstring from a parameter spec

    Amends any existing docstring of a callable with a textual
    description of its parameters. The Parameter spec needs to match
    the number and names of the callables arguments.
    """
    from inspect import getargspec
    # get the signature
    ndefaults = 0
    args, varargs, varkw, defaults = getargspec(func)
    if add_args:
        add_argnames = sorted(add_args.keys())
        args.extend(add_argnames)
        defaults = defaults + tuple(add_args[k] for k in add_argnames)
    if defaults is not None:
        ndefaults = len(defaults)
    # start documentation with what the callable brings with it
    doc = prefix if prefix else u''
    if len(args) > 1:
        if len(doc):
            doc += '\n'
        doc += "Parameters\n----------\n"
        for i, arg in enumerate(args):
            if not is_api_arg(arg):
                continue
            # we need a parameter spec for each argument
            if not arg in params:
                raise ValueError("function has argument '%s' not described as a parameter" % arg)
            param = params[arg]
            # validate the default -- to make sure that the parameter description is
            # somewhat OK
            defaults_idx = ndefaults - len(args) + i
            if defaults_idx >= 0:
                if param.constraints is not None:
                    param.constraints(defaults[defaults_idx])
            orig_docs = param._doc
            param._doc = alter_interface_docs_for_api(param._doc)
            doc += param.get_autodoc(
                arg,
                default=defaults[defaults_idx] if defaults_idx >= 0 else None,
                has_default=defaults_idx >= 0)
            param._doc = orig_docs
            doc += '\n'
    doc += suffix if suffix else u""
    # assign the amended docs
    func.__doc__ = doc
    return func


def build_doc(cls, **kwargs):
    """Decorator to build docstrings for datalad commands

    It's intended to decorate the class, the __call__-method of which is the
    actual command. It expects that __call__-method to be decorated by
    eval_results.

    Parameters
    ----------
    cls: Interface
      class defining a datalad command
    """

    # Note, that this is a class decorator, which is executed only once when the
    # class is imported. It builds the docstring for the class' __call__ method
    # and returns the original class.
    #
    # This is because a decorator for the actual function would not be able to
    # behave like this. To build the docstring we need to access the attribute
    # _params of the class. From within a function decorator we cannot do this
    # during import time, since the class is being built in this very moment and
    # is not yet available in the module. And if we do it from within the part
    # of a function decorator, that is executed when the function is called, we
    # would need to actually call the command once in order to build this
    # docstring.

    lgr.debug("Building doc for {}".format(cls))

    cls_doc = cls.__doc__
    if hasattr(cls, '_docs_'):
        # expand docs
        cls_doc = cls_doc.format(**cls._docs_)

    call_doc = None
    # suffix for update_docstring_with_parameters:
    if cls.__call__.__doc__:
        call_doc = cls.__call__.__doc__

    # build standard doc and insert eval_doc
    spec = getattr(cls, '_params_', dict())
    # get docs for eval_results parameters:
    spec.update(eval_params)

    update_docstring_with_parameters(
        cls.__call__, spec,
        prefix=alter_interface_docs_for_api(cls_doc),
        suffix=alter_interface_docs_for_api(call_doc),
        add_args=eval_defaults if not hasattr(cls, '_no_eval_results') else None
    )

    # return original
    return cls


class Interface(object):
    """Base class for interface implementations"""

    # exit code to return if user-interrupted
    # if None, would just reraise the Exception, so if in --dbg
    # mode would fall into the debugger
    _interrupted_exit_code = 1

    _OLDSTYLE_COMMANDS = (
        'AddArchiveContent', 'CrawlInit', 'Crawl', 'CreateSiblingGithub',
        'CreateTestDataset', 'DownloadURL', 'Export', 'Ls', 'Move', 'SSHRun',
        'Test')

    @classmethod
    def setup_parser(cls, parser):
        # XXX needs safety check for name collisions
        # XXX allow for parser kwargs customization
        parser_kwargs = {}
        from inspect import getargspec
        # get the signature
        ndefaults = 0
        args, varargs, varkw, defaults = getargspec(cls.__call__)
        if not defaults is None:
            ndefaults = len(defaults)
        for i, arg in enumerate(args):
            if not is_api_arg(arg):
                continue
            param = cls._params_[arg]
            defaults_idx = ndefaults - len(args) + i
            cmd_args = param.cmd_args
            if cmd_args is None:
                cmd_args = []
            if not len(cmd_args):
                if defaults_idx >= 0:
                    # dealing with a kwarg
                    template = '--%s'
                else:
                    # positional arg
                    template = '%s'
                # use parameter name as default argument name
                parser_args = (template % arg.replace('_', '-'),)
            else:
                parser_args = [c.replace('_', '-') for c in cmd_args]
            parser_kwargs = param.cmd_kwargs
            if defaults_idx >= 0:
                parser_kwargs['default'] = defaults[defaults_idx]
            help = alter_interface_docs_for_cmdline(param._doc)
            if help and help[-1] != '.':
                help += '.'
            if param.constraints is not None:
                parser_kwargs['type'] = param.constraints
                # include value constraint description and default
                # into the help string
                cdoc = alter_interface_docs_for_cmdline(
                    param.constraints.long_description())
                if cdoc[0] == '(' and cdoc[-1] == ')':
                    cdoc = cdoc[1:-1]
                help += '  Constraints: %s' % cdoc
            if defaults_idx >= 0:
                help += " [Default: %r]" % (defaults[defaults_idx],)
            # create the parameter, using the constraint instance for type
            # conversion
            parser.add_argument(*parser_args, help=help,
                                **parser_kwargs)

    @classmethod
    def call_from_parser(cls, args):
        # XXX needs safety check for name collisions
        from inspect import getargspec
        argspec = getargspec(cls.__call__)
        if argspec[2] is None:
            # no **kwargs in the call receiver, pull argnames from signature
            argnames = getargspec(cls.__call__)[0]
        else:
            # common options
            # XXX define or better get from elsewhere
            common_opts = ('change_path', 'common_debug', 'common_idebug', 'func',
                           'help', 'log_level', 'logger', 'pbs_runner',
                           '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 if 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 cls.__name__ not in Interface._OLDSTYLE_COMMANDS:
            # 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
            # default is requested
            kwargs['result_renderer'] = \
                args.common_output_format if args.common_output_format != 'default' \
                else getattr(cls, 'result_renderer', args.common_output_format)
            if '{' in args.common_output_format:
                # stupid hack, could and should become more powerful
                kwargs['result_renderer'] = \
                    lambda x, **kwargs: ui.message(args.common_output_format.format(
                        **{k: {k_.replace(':', '#'): v_ for k_, v_ in v.items()}
                           if isinstance(v, dict) else v
                           for k, v in x.items()}))
            if args.common_on_failure:
                kwargs['on_failure'] = args.common_on_failure
            # compose filter function from to be invented cmdline options
            res_filter = cls._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
        try:
            ret = cls.__call__(**kwargs)
            if inspect.isgenerator(ret):
                ret = list(ret)
            if args.common_output_format == 'tailored' and \
                    hasattr(cls, 'custom_result_summary_renderer'):
                cls.custom_result_summary_renderer(ret)
            return ret
        except KeyboardInterrupt as exc:
            ui.error("\nInterrupted by user while doing magic: %s" % exc_str(exc))
            if cls._interrupted_exit_code is not None:
                sys.exit(cls._interrupted_exit_code)
            else:
                raise

    @classmethod
    def _get_result_filter(cls, args):
        from datalad import cfg
        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

    @classmethod
    def get_refds_path(cls, dataset):
        """Return a resolved reference dataset path from a `dataset` argument"""
        # theoretically a dataset could come in as a relative path -> resolve
        if dataset is None:
            return dataset
        refds_path = dataset.path if isinstance(dataset, Dataset) \
            else Dataset(dataset).path
        if refds_path:
            refds_path = resolve_path(refds_path)
        return refds_path


def get_allargs_as_kwargs(call, args, kwargs):
    """Generate a kwargs dict from a call signature and *args, **kwargs

    Basically resolving the argnames for all positional arguments, and
    resolvin the defaults for all kwargs that are not given in a kwargs
    dict
    """
    from inspect import getargspec
    argspec = getargspec(call)
    defaults = argspec.defaults
    nargs = len(argspec.args)
    assert (nargs >= len(defaults))
    # map any args to their name
    argmap = list(zip(argspec.args[:len(args)], args))
    kwargs_ = OrderedDict(argmap)
    # map defaults of kwargs to their names (update below)
    for k, v in zip(argspec.args[-len(defaults):], defaults):
        if k not in kwargs_:
            kwargs_[k] = v
    # update with provided kwarg args
    kwargs_.update(kwargs)
    # XXX we cannot assert the following, because our own highlevel
    # API commands support more kwargs than what is discoverable
    # from their signature...
    #assert (nargs == len(kwargs_))
    return kwargs_

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
[ISSUE]
**Title:** Greedy Parsing of 'REFLOW' Tags Prevents Proper Documentation Formatting

**Description:**
When documentation contains multiple `REFLOW` sections, the parser fails to correctly identify the closing tags. This results in `REFLOW` tags remaining in the processed documentation, leading to formatting issues and unexpected behavior.

**Example Code:**
```python
docs = """
|| REFLOW >>
reflowed paragraph
<< REFLOW ||

  - a
  - b

|| REFLOW >>
another reflowed paragraph
<< REFLOW ||
"""

processed_docs = alter_interface_docs_for_api(docs)
print(processed_docs)
```

**Expected Behavior:**
Each `|| REFLOW >>` section should be individually processed, and all `REFLOW` tags should be removed after processing. The output should display correctly reflowed paragraphs without any remaining `REFLOW` tags.

**Actual Behavior:**
The parser does not stop at the first closing `<< REFLOW ||` tag due to greedy matching. As a result, `REFLOW` tags remain in the output, causing formatting errors in the documentation.

[/ISSUE]
{
    "test_dedent": "PASSED",
    "test_alter_interface_docs_for_api": "PASSED",
    "test_alter_interface_docs_for_cmdline": "PASSED"
}
diff --git a/datalad/interface/base.py b/datalad/interface/base.py
index bed8339b8..c2f351d41 100644
--- a/datalad/interface/base.py
+++ b/datalad/interface/base.py
@@ -99,7 +99,7 @@ def alter_interface_docs_for_api(docs):
     docs = dedent_docstring(docs)
     # clean cmdline sections
     docs = re.sub(
-        '\|\| CMDLINE \>\>.*\<\< CMDLINE \|\|',
+        '\|\| CMDLINE \>\>.*?\<\< CMDLINE \|\|',
         '',
         docs,
         flags=re.MULTILINE | re.DOTALL)
@@ -115,12 +115,12 @@ def alter_interface_docs_for_api(docs):
         docs,
         flags=re.MULTILINE)
     docs = re.sub(
-        '\|\| PYTHON \>\>(.*)\<\< PYTHON \|\|',
+        '\|\| PYTHON \>\>(.*?)\<\< PYTHON \|\|',
         lambda match: match.group(1),
         docs,
         flags=re.MULTILINE | re.DOTALL)
     docs = re.sub(
-        '\|\| REFLOW \>\>\n(.*)\<\< REFLOW \|\|',
+        '\|\| REFLOW \>\>\n(.*?)\<\< REFLOW \|\|',
         lambda match: textwrap.fill(match.group(1)),
         docs,
         flags=re.MULTILINE | re.DOTALL)
@@ -136,7 +136,7 @@ def alter_interface_docs_for_cmdline(docs):
     docs = dedent_docstring(docs)
     # clean cmdline sections
     docs = re.sub(
-        '\|\| PYTHON \>\>.*\<\< PYTHON \|\|',
+        '\|\| PYTHON \>\>.*?\<\< PYTHON \|\|',
         '',
         docs,
         flags=re.MULTILINE | re.DOTALL)
@@ -152,7 +152,7 @@ def alter_interface_docs_for_cmdline(docs):
         docs,
         flags=re.MULTILINE)
     docs = re.sub(
-        '\|\| CMDLINE \>\>(.*)\<\< CMDLINE \|\|',
+        '\|\| CMDLINE \>\>(.*?)\<\< CMDLINE \|\|',
         lambda match: match.group(1),
         docs,
         flags=re.MULTILINE | re.DOTALL)
@@ -188,7 +188,7 @@ def alter_interface_docs_for_cmdline(docs):
         docs,
         flags=re.MULTILINE)
     docs = re.sub(
-        '\|\| REFLOW \>\>\n(.*)\<\< REFLOW \|\|',
+        '\|\| REFLOW \>\>\n(.*?)\<\< REFLOW \|\|',
         lambda match: textwrap.fill(match.group(1)),
         docs,
         flags=re.MULTILINE | re.DOTALL)

{"datalad/interface/base.py": "# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-\n# ex: set sts=4 ts=4 sw=4 noet:\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n#\n#   See COPYING file distributed along with the datalad package for the\n#   copyright and license terms.\n#\n# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##\n\"\"\"High-level interface generation\n\n\"\"\"\n\n__docformat__ = 'restructuredtext'\n\nimport logging\nlgr = logging.getLogger('datalad.interface.base')\n\nimport sys\nimport re\nimport textwrap\nimport inspect\nfrom collections import OrderedDict\n\nfrom ..ui import ui\nfrom ..dochelpers import exc_str\n\nfrom datalad.interface.common_opts import eval_params\nfrom datalad.interface.common_opts import eval_defaults\nfrom datalad.support.constraints import EnsureKeyChoice\nfrom datalad.distribution.dataset import Dataset\nfrom datalad.distribution.dataset import resolve_path\n\n\ndefault_logchannels = {\n    '': 'debug',\n    'ok': 'debug',\n    'notneeded': 'debug',\n    'impossible': 'warning',\n    'error': 'error',\n}\n\n\ndef get_api_name(intfspec):\n    \"\"\"Given an interface specification return an API name for it\"\"\"\n    if len(intfspec) > 3:\n        name = intfspec[3]\n    else:\n        name = intfspec[0].split('.')[-1]\n    return name\n\n\ndef get_cmdline_command_name(intfspec):\n    \"\"\"Given an interface specification return a cmdline command name\"\"\"\n    if len(intfspec) > 2:\n        name = intfspec[2]\n    else:\n        name = intfspec[0].split('.')[-1].replace('_', '-')\n    return name\n\n\ndef get_interface_groups():\n    from .. import interface as _interfaces\n\n    grps = []\n    # auto detect all available interfaces and generate a function-based\n    # API from them\n    for _item in _interfaces.__dict__:\n        if not _item.startswith('_group_'):\n            continue\n        grp_name = _item[7:]\n        grp = getattr(_interfaces, _item)\n        grps.append((grp_name,) + grp)\n    return grps\n\n\ndef dedent_docstring(text):\n    \"\"\"Remove uniform indentation from a multiline docstring\"\"\"\n    # Problem is that first line might often have no offset, so might\n    # need to be ignored from dedent call\n    if text is None:\n        return None\n    if not text.startswith(' '):\n        lines = text.split('\\n')\n        if len(lines) == 1:\n            # single line, no indentation, nothing to do\n            return text\n        text2 = '\\n'.join(lines[1:])\n        return lines[0] + \"\\n\" + textwrap.dedent(text2)\n    else:\n        return textwrap.dedent(text)\n\n\ndef alter_interface_docs_for_api(docs):\n    \"\"\"Apply modifications to interface docstrings for Python API use.\"\"\"\n    # central place to alter the impression of docstrings,\n    # like removing cmdline specific sections\n    if not docs:\n        return docs\n    docs = dedent_docstring(docs)\n    # clean cmdline sections\n    docs = re.sub(\n        '\\|\\| CMDLINE \\>\\>.*\\<\\< CMDLINE \\|\\|',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # clean cmdline in-line bits\n    docs = re.sub(\n        '\\[CMD:\\s[^\\[\\]]*\\sCMD\\]',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    docs = re.sub(\n        '\\[PY:\\s([^\\[\\]]*)\\sPY\\]',\n        lambda match: match.group(1),\n        docs,\n        flags=re.MULTILINE)\n    docs = re.sub(\n        '\\|\\| PYTHON \\>\\>(.*)\\<\\< PYTHON \\|\\|',\n        lambda match: match.group(1),\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    docs = re.sub(\n        '\\|\\| REFLOW \\>\\>\\n(.*)\\<\\< REFLOW \\|\\|',\n        lambda match: textwrap.fill(match.group(1)),\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    return docs\n\n\ndef alter_interface_docs_for_cmdline(docs):\n    \"\"\"Apply modifications to interface docstrings for cmdline doc use.\"\"\"\n    # central place to alter the impression of docstrings,\n    # like removing Python API specific sections, and argument markup\n    if not docs:\n        return docs\n    docs = dedent_docstring(docs)\n    # clean cmdline sections\n    docs = re.sub(\n        '\\|\\| PYTHON \\>\\>.*\\<\\< PYTHON \\|\\|',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # clean cmdline in-line bits\n    docs = re.sub(\n        '\\[PY:\\s[^\\[\\]]*\\sPY\\]',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    docs = re.sub(\n        '\\[CMD:\\s([^\\[\\]]*)\\sCMD\\]',\n        lambda match: match.group(1),\n        docs,\n        flags=re.MULTILINE)\n    docs = re.sub(\n        '\\|\\| CMDLINE \\>\\>(.*)\\<\\< CMDLINE \\|\\|',\n        lambda match: match.group(1),\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # remove :role:`...` RST markup for cmdline docs\n    docs = re.sub(\n        r':\\S+:`[^`]*`[\\\\]*',\n        lambda match: ':'.join(match.group(0).split(':')[2:]).strip('`\\\\'),\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # remove None constraint. In general, `None` on the cmdline means don't\n    # give option at all, but specifying `None` explicitly is practically\n    # impossible\n    docs = re.sub(\n        ',\\sor\\svalue\\smust\\sbe\\s`None`',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # capitalize variables and remove backticks to uniformize with\n    # argparse output\n    docs = re.sub(\n        '`\\S*`',\n        lambda match: match.group(0).strip('`').upper(),\n        docs)\n    # clean up sphinx API refs\n    docs = re.sub(\n        '\\~datalad\\.api\\.\\S*',\n        lambda match: \"`{0}`\".format(match.group(0)[13:]),\n        docs)\n    # Remove RST paragraph markup\n    docs = re.sub(\n        r'^.. \\S+::',\n        lambda match: match.group(0)[3:-2].upper(),\n        docs,\n        flags=re.MULTILINE)\n    docs = re.sub(\n        '\\|\\| REFLOW \\>\\>\\n(.*)\\<\\< REFLOW \\|\\|',\n        lambda match: textwrap.fill(match.group(1)),\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    return docs\n\n\ndef is_api_arg(arg):\n    \"\"\"Return True if argument is our API argument or self or used for internal\n    purposes\n    \"\"\"\n    return arg != 'self' and not arg.startswith('_')\n\n\ndef update_docstring_with_parameters(func, params, prefix=None, suffix=None,\n                                     add_args=None):\n    \"\"\"Generate a useful docstring from a parameter spec\n\n    Amends any existing docstring of a callable with a textual\n    description of its parameters. The Parameter spec needs to match\n    the number and names of the callables arguments.\n    \"\"\"\n    from inspect import getargspec\n    # get the signature\n    ndefaults = 0\n    args, varargs, varkw, defaults = getargspec(func)\n    if add_args:\n        add_argnames = sorted(add_args.keys())\n        args.extend(add_argnames)\n        defaults = defaults + tuple(add_args[k] for k in add_argnames)\n    if defaults is not None:\n        ndefaults = len(defaults)\n    # start documentation with what the callable brings with it\n    doc = prefix if prefix else u''\n    if len(args) > 1:\n        if len(doc):\n            doc += '\\n'\n        doc += \"Parameters\\n----------\\n\"\n        for i, arg in enumerate(args):\n            if not is_api_arg(arg):\n                continue\n            # we need a parameter spec for each argument\n            if not arg in params:\n                raise ValueError(\"function has argument '%s' not described as a parameter\" % arg)\n            param = params[arg]\n            # validate the default -- to make sure that the parameter description is\n            # somewhat OK\n            defaults_idx = ndefaults - len(args) + i\n            if defaults_idx >= 0:\n                if param.constraints is not None:\n                    param.constraints(defaults[defaults_idx])\n            orig_docs = param._doc\n            param._doc = alter_interface_docs_for_api(param._doc)\n            doc += param.get_autodoc(\n                arg,\n                default=defaults[defaults_idx] if defaults_idx >= 0 else None,\n                has_default=defaults_idx >= 0)\n            param._doc = orig_docs\n            doc += '\\n'\n    doc += suffix if suffix else u\"\"\n    # assign the amended docs\n    func.__doc__ = doc\n    return func\n\n\ndef build_doc(cls, **kwargs):\n    \"\"\"Decorator to build docstrings for datalad commands\n\n    It's intended to decorate the class, the __call__-method of which is the\n    actual command. It expects that __call__-method to be decorated by\n    eval_results.\n\n    Parameters\n    ----------\n    cls: Interface\n      class defining a datalad command\n    \"\"\"\n\n    # Note, that this is a class decorator, which is executed only once when the\n    # class is imported. It builds the docstring for the class' __call__ method\n    # and returns the original class.\n    #\n    # This is because a decorator for the actual function would not be able to\n    # behave like this. To build the docstring we need to access the attribute\n    # _params of the class. From within a function decorator we cannot do this\n    # during import time, since the class is being built in this very moment and\n    # is not yet available in the module. And if we do it from within the part\n    # of a function decorator, that is executed when the function is called, we\n    # would need to actually call the command once in order to build this\n    # docstring.\n\n    lgr.debug(\"Building doc for {}\".format(cls))\n\n    cls_doc = cls.__doc__\n    if hasattr(cls, '_docs_'):\n        # expand docs\n        cls_doc = cls_doc.format(**cls._docs_)\n\n    call_doc = None\n    # suffix for update_docstring_with_parameters:\n    if cls.__call__.__doc__:\n        call_doc = cls.__call__.__doc__\n\n    # build standard doc and insert eval_doc\n    spec = getattr(cls, '_params_', dict())\n    # get docs for eval_results parameters:\n    spec.update(eval_params)\n\n    update_docstring_with_parameters(\n        cls.__call__, spec,\n        prefix=alter_interface_docs_for_api(cls_doc),\n        suffix=alter_interface_docs_for_api(call_doc),\n        add_args=eval_defaults if not hasattr(cls, '_no_eval_results') else None\n    )\n\n    # return original\n    return cls\n\n\nclass Interface(object):\n    \"\"\"Base class for interface implementations\"\"\"\n\n    # exit code to return if user-interrupted\n    # if None, would just reraise the Exception, so if in --dbg\n    # mode would fall into the debugger\n    _interrupted_exit_code = 1\n\n    _OLDSTYLE_COMMANDS = (\n        'AddArchiveContent', 'CrawlInit', 'Crawl', 'CreateSiblingGithub',\n        'CreateTestDataset', 'DownloadURL', 'Export', 'Ls', 'Move', 'SSHRun',\n        'Test')\n\n    @classmethod\n    def setup_parser(cls, parser):\n        # XXX needs safety check for name collisions\n        # XXX allow for parser kwargs customization\n        parser_kwargs = {}\n        from inspect import getargspec\n        # get the signature\n        ndefaults = 0\n        args, varargs, varkw, defaults = getargspec(cls.__call__)\n        if not defaults is None:\n            ndefaults = len(defaults)\n        for i, arg in enumerate(args):\n            if not is_api_arg(arg):\n                continue\n            param = cls._params_[arg]\n            defaults_idx = ndefaults - len(args) + i\n            cmd_args = param.cmd_args\n            if cmd_args is None:\n                cmd_args = []\n            if not len(cmd_args):\n                if defaults_idx >= 0:\n                    # dealing with a kwarg\n                    template = '--%s'\n                else:\n                    # positional arg\n                    template = '%s'\n                # use parameter name as default argument name\n                parser_args = (template % arg.replace('_', '-'),)\n            else:\n                parser_args = [c.replace('_', '-') for c in cmd_args]\n            parser_kwargs = param.cmd_kwargs\n            if defaults_idx >= 0:\n                parser_kwargs['default'] = defaults[defaults_idx]\n            help = alter_interface_docs_for_cmdline(param._doc)\n            if help and help[-1] != '.':\n                help += '.'\n            if param.constraints is not None:\n                parser_kwargs['type'] = param.constraints\n                # include value constraint description and default\n                # into the help string\n                cdoc = alter_interface_docs_for_cmdline(\n                    param.constraints.long_description())\n                if cdoc[0] == '(' and cdoc[-1] == ')':\n                    cdoc = cdoc[1:-1]\n                help += '  Constraints: %s' % cdoc\n            if defaults_idx >= 0:\n                help += \" [Default: %r]\" % (defaults[defaults_idx],)\n            # create the parameter, using the constraint instance for type\n            # conversion\n            parser.add_argument(*parser_args, help=help,\n                                **parser_kwargs)\n\n    @classmethod\n    def call_from_parser(cls, args):\n        # XXX needs safety check for name collisions\n        from inspect import getargspec\n        argspec = getargspec(cls.__call__)\n        if argspec[2] is None:\n            # no **kwargs in the call receiver, pull argnames from signature\n            argnames = getargspec(cls.__call__)[0]\n        else:\n            # common options\n            # XXX define or better get from elsewhere\n            common_opts = ('change_path', 'common_debug', 'common_idebug', 'func',\n                           'help', 'log_level', 'logger', 'pbs_runner',\n                           'result_renderer', 'subparser')\n            argnames = [name for name in dir(args)\n                        if not (name.startswith('_') or name in common_opts)]\n        kwargs = {k: getattr(args, k) for k in argnames if is_api_arg(k)}\n        # we are coming from the entry point, this is the toplevel command,\n        # let it run like generator so we can act on partial results quicker\n        # TODO remove following condition test when transition is complete and\n        # run indented code unconditionally\n        if cls.__name__ not in Interface._OLDSTYLE_COMMANDS:\n            # set all common args explicitly  to override class defaults\n            # that are tailored towards the the Python API\n            kwargs['return_type'] = 'generator'\n            kwargs['result_xfm'] = None\n            # allow commands to override the default, unless something other than\n            # default is requested\n            kwargs['result_renderer'] = \\\n                args.common_output_format if args.common_output_format != 'default' \\\n                else getattr(cls, 'result_renderer', args.common_output_format)\n            if '{' in args.common_output_format:\n                # stupid hack, could and should become more powerful\n                kwargs['result_renderer'] = \\\n                    lambda x, **kwargs: ui.message(args.common_output_format.format(\n                        **{k: {k_.replace(':', '#'): v_ for k_, v_ in v.items()}\n                           if isinstance(v, dict) else v\n                           for k, v in x.items()}))\n            if args.common_on_failure:\n                kwargs['on_failure'] = args.common_on_failure\n            # compose filter function from to be invented cmdline options\n            res_filter = cls._get_result_filter(args)\n            if res_filter is not None:\n                # Don't add result_filter if it's None because then\n                # eval_results can't distinguish between --report-{status,type}\n                # not specified via the CLI and None passed via the Python API.\n                kwargs['result_filter'] = res_filter\n        try:\n            ret = cls.__call__(**kwargs)\n            if inspect.isgenerator(ret):\n                ret = list(ret)\n            if args.common_output_format == 'tailored' and \\\n                    hasattr(cls, 'custom_result_summary_renderer'):\n                cls.custom_result_summary_renderer(ret)\n            return ret\n        except KeyboardInterrupt as exc:\n            ui.error(\"\\nInterrupted by user while doing magic: %s\" % exc_str(exc))\n            if cls._interrupted_exit_code is not None:\n                sys.exit(cls._interrupted_exit_code)\n            else:\n                raise\n\n    @classmethod\n    def _get_result_filter(cls, args):\n        from datalad import cfg\n        result_filter = None\n        if args.common_report_status or 'datalad.runtime.report-status' in cfg:\n            report_status = args.common_report_status or \\\n                            cfg.obtain('datalad.runtime.report-status')\n            if report_status == \"all\":\n                pass  # no filter\n            elif report_status == 'success':\n                result_filter = EnsureKeyChoice('status', ('ok', 'notneeded'))\n            elif report_status == 'failure':\n                result_filter = EnsureKeyChoice('status',\n                                                ('impossible', 'error'))\n            else:\n                result_filter = EnsureKeyChoice('status', (report_status,))\n        if args.common_report_type:\n            tfilt = EnsureKeyChoice('type', tuple(args.common_report_type))\n            result_filter = result_filter & tfilt if result_filter else tfilt\n        return result_filter\n\n    @classmethod\n    def get_refds_path(cls, dataset):\n        \"\"\"Return a resolved reference dataset path from a `dataset` argument\"\"\"\n        # theoretically a dataset could come in as a relative path -> resolve\n        if dataset is None:\n            return dataset\n        refds_path = dataset.path if isinstance(dataset, Dataset) \\\n            else Dataset(dataset).path\n        if refds_path:\n            refds_path = resolve_path(refds_path)\n        return refds_path\n\n\ndef get_allargs_as_kwargs(call, args, kwargs):\n    \"\"\"Generate a kwargs dict from a call signature and *args, **kwargs\n\n    Basically resolving the argnames for all positional arguments, and\n    resolvin the defaults for all kwargs that are not given in a kwargs\n    dict\n    \"\"\"\n    from inspect import getargspec\n    argspec = getargspec(call)\n    defaults = argspec.defaults\n    nargs = len(argspec.args)\n    assert (nargs >= len(defaults))\n    # map any args to their name\n    argmap = list(zip(argspec.args[:len(args)], args))\n    kwargs_ = OrderedDict(argmap)\n    # map defaults of kwargs to their names (update below)\n    for k, v in zip(argspec.args[-len(defaults):], defaults):\n        if k not in kwargs_:\n            kwargs_[k] = v\n    # update with provided kwarg args\n    kwargs_.update(kwargs)\n    # XXX we cannot assert the following, because our own highlevel\n    # API commands support more kwargs than what is discoverable\n    # from their signature...\n    #assert (nargs == len(kwargs_))\n    return kwargs_\n"}
A user will ask you to solve a task. You should first draft your thinking process (inner monologue). Then, generate the solution.

Your response format must follow the template below:
<think>
Your thoughts or/and draft, like working through an exercise on scratch paper. Be as casual and as long as you want until you are confident to generate a correct solution.
</think>
<solution>
Final solution presented to the user.
</solution>
system
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** Greedy Parsing of 'REFLOW' Tags Prevents Proper Documentation Formatting

**Description:**
When documentation contains multiple `REFLOW` sections, the parser fails to correctly identify the closing tags. This results in `REFLOW` tags remaining in the processed documentation, leading to formatting issues and unexpected behavior.

**Example Code:**
```python
docs = """
|| REFLOW >>
reflowed paragraph
<< REFLOW ||

  - a
  - b

|| REFLOW >>
another reflowed paragraph
<< REFLOW ||
"""

processed_docs = alter_interface_docs_for_api(docs)
print(processed_docs)
```

**Expected Behavior:**
Each `|| REFLOW >>` section should be individually processed, and all `REFLOW` tags should be removed after processing. The output should display correctly reflowed paragraphs without any remaining `REFLOW` tags.

**Actual Behavior:**
The parser does not stop at the first closing `<< REFLOW ||` tag due to greedy matching. As a result, `REFLOW` tags remain in the output, causing formatting errors in the documentation.

[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### datalad/interface/base.py
# emacs: -*- mode: python; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: nil -*-
# ex: set sts=4 ts=4 sw=4 noet:
# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
#
#   See COPYING file distributed along with the datalad package for the
#   copyright and license terms.
#
# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
"""High-level interface generation

"""

__docformat__ = 'restructuredtext'

import logging
lgr = logging.getLogger('datalad.interface.base')

import sys
import re
import textwrap
import inspect
from collections import OrderedDict

from ..ui import ui
from ..dochelpers import exc_str

from datalad.interface.common_opts import eval_params
from datalad.interface.common_opts import eval_defaults
from datalad.support.constraints import EnsureKeyChoice
from datalad.distribution.dataset import Dataset
from datalad.distribution.dataset import resolve_path


default_logchannels = {
    '': 'debug',
    'ok': 'debug',
    'notneeded': 'debug',
    'impossible': 'warning',
    'error': 'error',
}


def get_api_name(intfspec):
    """Given an interface specification return an API name for it"""
    if len(intfspec) > 3:
        name = intfspec[3]
    else:
        name = intfspec[0].split('.')[-1]
    return name


def get_cmdline_command_name(intfspec):
    """Given an interface specification return a cmdline command name"""
    if len(intfspec) > 2:
        name = intfspec[2]
    else:
        name = intfspec[0].split('.')[-1].replace('_', '-')
    return name


def get_interface_groups():
    from .. import interface as _interfaces

    grps = []
    # auto detect all available interfaces and generate a function-based
    # API from them
    for _item in _interfaces.__dict__:
        if not _item.startswith('_group_'):
            continue
        grp_name = _item[7:]
        grp = getattr(_interfaces, _item)
        grps.append((grp_name,) + grp)
    return grps


def dedent_docstring(text):
    """Remove uniform indentation from a multiline docstring"""
    # Problem is that first line might often have no offset, so might
    # need to be ignored from dedent call
    if text is None:
        return None
    if not text.startswith(' '):
        lines = text.split('\n')
        if len(lines) == 1:
            # single line, no indentation, nothing to do
            return text
        text2 = '\n'.join(lines[1:])
        return lines[0] + "\n" + textwrap.dedent(text2)
    else:
        return textwrap.dedent(text)


def alter_interface_docs_for_api(docs):
    """Apply modifications to interface docstrings for Python API use."""
    # central place to alter the impression of docstrings,
    # like removing cmdline specific sections
    if not docs:
        return docs
    docs = dedent_docstring(docs)
    # clean cmdline sections
    docs = re.sub(
        '\|\| CMDLINE \>\>.*\<\< CMDLINE \|\|',
        '',
        docs,
        flags=re.MULTILINE | re.DOTALL)
    # clean cmdline in-line bits
    docs = re.sub(
        '\[CMD:\s[^\[\]]*\sCMD\]',
        '',
        docs,
        flags=re.MULTILINE | re.DOTALL)
    docs = re.sub(
        '\[PY:\s([^\[\]]*)\sPY\]',
        lambda match: match.group(1),
        docs,
        flags=re.MULTILINE)
    docs = re.sub(
        '\|\| PYTHON \>\>(.*)\<\< PYTHON \|\|',
        lambda match: match.group(1),
        docs,
        flags=re.MULTILINE | re.DOTALL)
    docs = re.sub(
        '\|\| REFLOW \>\>\n(.*)\<\< REFLOW \|\|',
        lambda match: textwrap.fill(match.group(1)),
        docs,
        flags=re.MULTILINE | re.DOTALL)
    return docs


def alter_interface_docs_for_cmdline(docs):
    """Apply modifications to interface docstrings for cmdline doc use."""
    # central place to alter the impression of docstrings,
    # like removing Python API specific sections, and argument markup
    if not docs:
        return docs
    docs = dedent_docstring(docs)
    # clean cmdline sections
    docs = re.sub(
        '\|\| PYTHON \>\>.*\<\< PYTHON \|\|',
        '',
        docs,
        flags=re.MULTILINE | re.DOTALL)
    # clean cmdline in-line bits
    docs = re.sub(
        '\[PY:\s[^\[\]]*\sPY\]',
        '',
        docs,
        flags=re.MULTILINE | re.DOTALL)
    docs = re.sub(
        '\[CMD:\s([^\[\]]*)\sCMD\]',
        lambda match: match.group(1),
        docs,
        flags=re.MULTILINE)
    docs = re.sub(
        '\|\| CMDLINE \>\>(.*)\<\< CMDLINE \|\|',
        lambda match: match.group(1),
        docs,
        flags=re.MULTILINE | re.DOTALL)
    # remove :role:`...` RST markup for cmdline docs
    docs = re.sub(
        r':\S+:`[^`]*`[\\]*',
        lambda match: ':'.join(match.group(0).split(':')[2:]).strip('`\\'),
        docs,
        flags=re.MULTILINE | re.DOTALL)
    # remove None constraint. In general, `None` on the cmdline means don't
    # give option at all, but specifying `None` explicitly is practically
    # impossible
    docs = re.sub(
        ',\sor\svalue\smust\sbe\s`None`',
        '',
        docs,
        flags=re.MULTILINE | re.DOTALL)
    # capitalize variables and remove backticks to uniformize with
    # argparse output
    docs = re.sub(
        '`\S*`',
        lambda match: match.group(0).strip('`').upper(),
        docs)
    # clean up sphinx API refs
    docs = re.sub(
        '\~datalad\.api\.\S*',
        lambda match: "`{0}`".format(match.group(0)[13:]),
        docs)
    # Remove RST paragraph markup
    docs = re.sub(
        r'^.. \S+::',
        lambda match: match.group(0)[3:-2].upper(),
        docs,
        flags=re.MULTILINE)
    docs = re.sub(
        '\|\| REFLOW \>\>\n(.*)\<\< REFLOW \|\|',
        lambda match: textwrap.fill(match.group(1)),
        docs,
        flags=re.MULTILINE | re.DOTALL)
    return docs


def is_api_arg(arg):
    """Return True if argument is our API argument or self or used for internal
    purposes
    """
    return arg != 'self' and not arg.startswith('_')


def update_docstring_with_parameters(func, params, prefix=None, suffix=None,
                                     add_args=None):
    """Generate a useful docstring from a parameter spec

    Amends any existing docstring of a callable with a textual
    description of its parameters. The Parameter spec needs to match
    the number and names of the callables arguments.
    """
    from inspect import getargspec
    # get the signature
    ndefaults = 0
    args, varargs, varkw, defaults = getargspec(func)
    if add_args:
        add_argnames = sorted(add_args.keys())
        args.extend(add_argnames)
        defaults = defaults + tuple(add_args[k] for k in add_argnames)
    if defaults is not None:
        ndefaults = len(defaults)
    # start documentation with what the callable brings with it
    doc = prefix if prefix else u''
    if len(args) > 1:
        if len(doc):
            doc += '\n'
        doc += "Parameters\n----------\n"
        for i, arg in enumerate(args):
            if not is_api_arg(arg):
                continue
            # we need a parameter spec for each argument
            if not arg in params:
                raise ValueError("function has argument '%s' not described as a parameter" % arg)
            param = params[arg]
            # validate the default -- to make sure that the parameter description is
            # somewhat OK
            defaults_idx = ndefaults - len(args) + i
            if defaults_idx >= 0:
                if param.constraints is not None:
                    param.constraints(defaults[defaults_idx])
            orig_docs = param._doc
            param._doc = alter_interface_docs_for_api(param._doc)
            doc += param.get_autodoc(
                arg,
                default=defaults[defaults_idx] if defaults_idx >= 0 else None,
                has_default=defaults_idx >= 0)
            param._doc = orig_docs
            doc += '\n'
    doc += suffix if suffix else u""
    # assign the amended docs
    func.__doc__ = doc
    return func


def build_doc(cls, **kwargs):
    """Decorator to build docstrings for datalad commands

    It's intended to decorate the class, the __call__-method of which is the
    actual command. It expects that __call__-method to be decorated by
    eval_results.

    Parameters
    ----------
    cls: Interface
      class defining a datalad command
    """

    # Note, that this is a class decorator, which is executed only once when the
    # class is imported. It builds the docstring for the class' __call__ method
    # and returns the original class.
    #
    # This is because a decorator for the actual function would not be able to
    # behave like this. To build the docstring we need to access the attribute
    # _params of the class. From within a function decorator we cannot do this
    # during import time, since the class is being built in this very moment and
    # is not yet available in the module. And if we do it from within the part
    # of a function decorator, that is executed when the function is called, we
    # would need to actually call the command once in order to build this
    # docstring.

    lgr.debug("Building doc for {}".format(cls))

    cls_doc = cls.__doc__
    if hasattr(cls, '_docs_'):
        # expand docs
        cls_doc = cls_doc.format(**cls._docs_)

    call_doc = None
    # suffix for update_docstring_with_parameters:
    if cls.__call__.__doc__:
        call_doc = cls.__call__.__doc__

    # build standard doc and insert eval_doc
    spec = getattr(cls, '_params_', dict())
    # get docs for eval_results parameters:
    spec.update(eval_params)

    update_docstring_with_parameters(
        cls.__call__, spec,
        prefix=alter_interface_docs_for_api(cls_doc),
        suffix=alter_interface_docs_for_api(call_doc),
        add_args=eval_defaults if not hasattr(cls, '_no_eval_results') else None
    )

    # return original
    return cls


class Interface(object):
    """Base class for interface implementations"""

    # exit code to return if user-interrupted
    # if None, would just reraise the Exception, so if in --dbg
    # mode would fall into the debugger
    _interrupted_exit_code = 1

    _OLDSTYLE_COMMANDS = (
        'AddArchiveContent', 'CrawlInit', 'Crawl', 'CreateSiblingGithub',
        'CreateTestDataset', 'DownloadURL', 'Export', 'Ls', 'Move', 'SSHRun',
        'Test')

    @classmethod
    def setup_parser(cls, parser):
        # XXX needs safety check for name collisions
        # XXX allow for parser kwargs customization
        parser_kwargs = {}
        from inspect import getargspec
        # get the signature
        ndefaults = 0
        args, varargs, varkw, defaults = getargspec(cls.__call__)
        if not defaults is None:
            ndefaults = len(defaults)
        for i, arg in enumerate(args):
            if not is_api_arg(arg):
                continue
            param = cls._params_[arg]
            defaults_idx = ndefaults - len(args) + i
            cmd_args = param.cmd_args
            if cmd_args is None:
                cmd_args = []
            if not len(cmd_args):
                if defaults_idx >= 0:
                    # dealing with a kwarg
                    template = '--%s'
                else:
                    # positional arg
                    template = '%s'
                # use parameter name as default argument name
                parser_args = (template % arg.replace('_', '-'),)
            else:
                parser_args = [c.replace('_', '-') for c in cmd_args]
            parser_kwargs = param.cmd_kwargs
            if defaults_idx >= 0:
                parser_kwargs['default'] = defaults[defaults_idx]
            help = alter_interface_docs_for_cmdline(param._doc)
            if help and help[-1] != '.':
                help += '.'
            if param.constraints is not None:
                parser_kwargs['type'] = param.constraints
                # include value constraint description and default
                # into the help string
                cdoc = alter_interface_docs_for_cmdline(
                    param.constraints.long_description())
                if cdoc[0] == '(' and cdoc[-1] == ')':
                    cdoc = cdoc[1:-1]
                help += '  Constraints: %s' % cdoc
            if defaults_idx >= 0:
                help += " [Default: %r]" % (defaults[defaults_idx],)
            # create the parameter, using the constraint instance for type
            # conversion
            parser.add_argument(*parser_args, help=help,
                                **parser_kwargs)

    @classmethod
    def call_from_parser(cls, args):
        # XXX needs safety check for name collisions
        from inspect import getargspec
        argspec = getargspec(cls.__call__)
        if argspec[2] is None:
            # no **kwargs in the call receiver, pull argnames from signature
            argnames = getargspec(cls.__call__)[0]
        else:
            # common options
            # XXX define or better get from elsewhere
            common_opts = ('change_path', 'common_debug', 'common_idebug', 'func',
                           'help', 'log_level', 'logger', 'pbs_runner',
                           '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 if 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 cls.__name__ not in Interface._OLDSTYLE_COMMANDS:
            # 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
            # default is requested
            kwargs['result_renderer'] = \
                args.common_output_format if args.common_output_format != 'default' \
                else getattr(cls, 'result_renderer', args.common_output_format)
            if '{' in args.common_output_format:
                # stupid hack, could and should become more powerful
                kwargs['result_renderer'] = \
                    lambda x, **kwargs: ui.message(args.common_output_format.format(
                        **{k: {k_.replace(':', '#'): v_ for k_, v_ in v.items()}
                           if isinstance(v, dict) else v
                           for k, v in x.items()}))
            if args.common_on_failure:
                kwargs['on_failure'] = args.common_on_failure
            # compose filter function from to be invented cmdline options
            res_filter = cls._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
        try:
            ret = cls.__call__(**kwargs)
            if inspect.isgenerator(ret):
                ret = list(ret)
            if args.common_output_format == 'tailored' and \
                    hasattr(cls, 'custom_result_summary_renderer'):
                cls.custom_result_summary_renderer(ret)
            return ret
        except KeyboardInterrupt as exc:
            ui.error("\nInterrupted by user while doing magic: %s" % exc_str(exc))
            if cls._interrupted_exit_code is not None:
                sys.exit(cls._interrupted_exit_code)
            else:
                raise

    @classmethod
    def _get_result_filter(cls, args):
        from datalad import cfg
        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

    @classmethod
    def get_refds_path(cls, dataset):
        """Return a resolved reference dataset path from a `dataset` argument"""
        # theoretically a dataset could come in as a relative path -> resolve
        if dataset is None:
            return dataset
        refds_path = dataset.path if isinstance(dataset, Dataset) \
            else Dataset(dataset).path
        if refds_path:
            refds_path = resolve_path(refds_path)
        return refds_path


def get_allargs_as_kwargs(call, args, kwargs):
    """Generate a kwargs dict from a call signature and *args, **kwargs

    Basically resolving the argnames for all positional arguments, and
    resolvin the defaults for all kwargs that are not given in a kwargs
    dict
    """
    from inspect import getargspec
    argspec = getargspec(call)
    defaults = argspec.defaults
    nargs = len(argspec.args)
    assert (nargs >= len(defaults))
    # map any args to their name
    argmap = list(zip(argspec.args[:len(args)], args))
    kwargs_ = OrderedDict(argmap)
    # map defaults of kwargs to their names (update below)
    for k, v in zip(argspec.args[-len(defaults):], defaults):
        if k not in kwargs_:
            kwargs_[k] = v
    # update with provided kwarg args
    kwargs_.update(kwargs)
    # XXX we cannot assert the following, because our own highlevel
    # API commands support more kwargs than what is discoverable
    # from their signature...
    #assert (nargs == len(kwargs_))
    return kwargs_

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
user