{
    "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 sys\nimport re\n\nfrom ..ui import ui\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    import textwrap\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        '\\|\\| Command line use only \\>\\>.*\\<\\< Command line use only \\|\\|',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # clean cmdline in-line bits\n    docs = re.sub(\n        '\\[CMD:\\s.*CMD\\]',\n        '',\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 use only \\>\\>.*\\<\\< Python use only \\|\\|',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # clean cmdline in-line bits\n    docs = re.sub(\n        '\\[PY:\\s.*PY\\]',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # remove :term:`...` RST markup for cmdline docs\n    docs = re.sub(\n        r':term:`.*`[\\\\]*',\n        lambda match: match.group(0)[7:].strip('`\\\\'),\n        docs)\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    return docs\n\n\ndef update_docstring_with_parameters(func, params, prefix=None, suffix=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 not defaults is 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 arg == 'self':\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 not param.constraints is None:\n                    param.constraints(defaults[defaults_idx])\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            doc += '\\n'\n    doc += suffix if suffix else u\"\"\n    # assign the amended docs\n    func.__doc__ = doc\n    return func\n\n\nclass Interface(object):\n    \"\"\"Base class for interface implementations\"\"\"\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 arg == 'self':\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 contraint 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        argnames = getargspec(cls.__call__)[0]\n        kwargs = {k: getattr(args, k) for k in argnames if k != 'self'}\n        try:\n            return cls.__call__(**kwargs)\n        except KeyboardInterrupt:\n            ui.error(\"\\nInterrupted by user while doing magic\")\n            sys.exit(1)\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 sys\nimport re\n\nfrom ..ui import ui\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    import textwrap\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        '\\|\\| Command line use only \\>\\>.*\\<\\< Command line use only \\|\\|',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # clean cmdline in-line bits\n    docs = re.sub(\n        '\\[CMD:\\s.*CMD\\]',\n        '',\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 use only \\>\\>.*\\<\\< Python use only \\|\\|',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # clean cmdline in-line bits\n    docs = re.sub(\n        '\\[PY:\\s.*PY\\]',\n        '',\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    return docs\n\n\ndef update_docstring_with_parameters(func, params, prefix=None, suffix=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 not defaults is 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 arg == 'self':\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 not param.constraints is None:\n                    param.constraints(defaults[defaults_idx])\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            doc += '\\n'\n    doc += suffix if suffix else u\"\"\n    # assign the amended docs\n    func.__doc__ = doc\n    return func\n\n\nclass Interface(object):\n    \"\"\"Base class for interface implementations\"\"\"\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 arg == 'self':\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 contraint 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        argnames = getargspec(cls.__call__)[0]\n        kwargs = {k: getattr(args, k) for k in argnames if k != 'self'}\n        try:\n            return cls.__call__(**kwargs)\n        except KeyboardInterrupt:\n            ui.error(\"\\nInterrupted by user while doing magic\")\n            sys.exit(1)\n",
            "header": {
                "file": {
                    "path": "datalad/interface/base.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "0acd42181",
                "new_commit_hash": "35aba6bed",
                "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": 110,
                            "length": 11
                        },
                        "new_range": {
                            "start": 110,
                            "length": 12
                        },
                        "section": "def alter_interface_docs_for_cmdline(docs):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        '',",
                                "type": "context"
                            },
                            {
                                "content": "        docs,",
                                "type": "context"
                            },
                            {
                                "content": "        flags=re.MULTILINE | re.DOTALL)",
                                "type": "context"
                            },
                            {
                                "content": "    # remove :term:`...` RST markup for cmdline docs",
                                "type": "deleted"
                            },
                            {
                                "content": "    # remove :role:`...` RST markup for cmdline docs",
                                "type": "added"
                            },
                            {
                                "content": "    docs = re.sub(",
                                "type": "context"
                            },
                            {
                                "content": "        r':term:`.*`[\\\\]*',",
                                "type": "deleted"
                            },
                            {
                                "content": "        lambda match: match.group(0)[7:].strip('`\\\\'),",
                                "type": "deleted"
                            },
                            {
                                "content": "        docs)",
                                "type": "deleted"
                            },
                            {
                                "content": "        r':\\S+:`[^`]*`[\\\\]*',",
                                "type": "added"
                            },
                            {
                                "content": "        lambda match: ':'.join(match.group(0).split(':')[2:]).strip('`\\\\'),",
                                "type": "added"
                            },
                            {
                                "content": "        docs,",
                                "type": "added"
                            },
                            {
                                "content": "        flags=re.MULTILINE | re.DOTALL)",
                                "type": "added"
                            },
                            {
                                "content": "    # remove None constraint. In general, `None` on the cmdline means don't",
                                "type": "context"
                            },
                            {
                                "content": "    # give option at all, but specifying `None` explicitly is practically",
                                "type": "context"
                            },
                            {
                                "content": "    # impossible",
                                "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 use only \\>\\>.*\\<\\< Python use only \\|\\|',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # clean cmdline in-line bits\n    docs = re.sub(\n        '\\[PY:\\s.*PY\\]',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # remove :term:`...` RST markup for cmdline docs\n    docs = re.sub(\n        r':term:`.*`[\\\\]*',\n        lambda match: match.group(0)[7:].strip('`\\\\'),\n        docs)\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    return docs",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 94,
                            "end_lineno": 132,
                            "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\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    || Command line use only >>\n    Something for the cmdline only CMDONLY\n    Multiline!\n    << Command line use only ||\n\n    || Python use only >>\n\n    Some Python-only bits PYONLY\n    Multiline!\n\n    << Python use only ||\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      specify the dataset to perform the install operation on. If no\n      dataset is given, an attempt is made to identify the dataset based\n      on the current working directory and/or the `path` given.\n      Constraints: Value must be a Dataset or a valid identifier of a\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('Command line', alt)\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('Python', alt)\n    # args\n    altarg = alter_interface_docs_for_cmdline(demo_argdoc)\n    # TO BE CONTINUED\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    || Command line use only >>\n    Something for the cmdline only CMDONLY\n    Multiline!\n    << Command line use only ||\n\n    || Python use only >>\n\n    Some Python-only bits PYONLY\n    Multiline!\n\n    << Python use only ||\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      specify the dataset to perform the install operation on. If no\n      dataset is given, an attempt is made to identify the dataset based\n      on the current working directory and/or the `path` given.\n      Constraints: Value must be a Dataset or a valid identifier of a\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('Command line', alt)\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('Python', 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",
            "header": {
                "file": {
                    "path": "datalad/interface/tests/test_docs.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "72a9f1f34",
                "new_commit_hash": "0c8b10b16",
                "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": 16,
                            "length": 7
                        },
                        "new_range": {
                            "start": 16,
                            "length": 7
                        },
                        "section": "from datalad.interface.base import dedent_docstring"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "from datalad.interface.base import alter_interface_docs_for_api",
                                "type": "context"
                            },
                            {
                                "content": "from datalad.interface.base import alter_interface_docs_for_cmdline",
                                "type": "context"
                            },
                            {
                                "content": "from datalad.tests.utils import assert_true, assert_false, assert_in, \\",
                                "type": "context"
                            },
                            {
                                "content": "    assert_not_in",
                                "type": "deleted"
                            },
                            {
                                "content": "    assert_not_in, eq_",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "demo_doc = \"\"\"\\",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [
                        {
                            "file_name": "datalad/interface/tests/test_docs.py",
                            "type": "statement",
                            "name": "",
                            "content": "from datalad.tests.utils import assert_true, assert_false, assert_in, \\\n    assert_not_in, eq_",
                            "ast_type_str": "ImportFrom",
                            "start_lineno": 18,
                            "end_lineno": 19,
                            "parent": null
                        }
                    ],
                    "deleted_entities": [
                        {
                            "file_name": "datalad/interface/tests/test_docs.py",
                            "type": "statement",
                            "name": "",
                            "content": "from datalad.tests.utils import assert_true, assert_false, assert_in, \\\n    assert_not_in",
                            "ast_type_str": "ImportFrom",
                            "start_lineno": 18,
                            "end_lineno": 19,
                            "parent": null
                        }
                    ]
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 95,
                            "length": 4
                        },
                        "new_range": {
                            "start": 95,
                            "length": 13
                        },
                        "section": "def test_alter_interface_docs_for_cmdline():"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    assert_not_in('Python', alt)",
                                "type": "context"
                            },
                            {
                                "content": "    # args",
                                "type": "context"
                            },
                            {
                                "content": "    altarg = alter_interface_docs_for_cmdline(demo_argdoc)",
                                "type": "context"
                            },
                            {
                                "content": "    # TO BE CONTINUED",
                                "type": "deleted"
                            },
                            {
                                "content": "    # RST role markup",
                                "type": "added"
                            },
                            {
                                "content": "    eq_(alter_interface_docs_for_cmdline(':murks:`me and my buddies`'),",
                                "type": "added"
                            },
                            {
                                "content": "        'me and my buddies')",
                                "type": "added"
                            },
                            {
                                "content": "    # spread across lines",
                                "type": "added"
                            },
                            {
                                "content": "    eq_(alter_interface_docs_for_cmdline(':term:`Barbara\\nStreisand`'),",
                                "type": "added"
                            },
                            {
                                "content": "        'Barbara\\nStreisand')",
                                "type": "added"
                            },
                            {
                                "content": "    # multiple on one line",
                                "type": "added"
                            },
                            {
                                "content": "    eq_(alter_interface_docs_for_cmdline(",
                                "type": "added"
                            },
                            {
                                "content": "        ':term:`one` bla bla :term:`two` bla'),",
                                "type": "added"
                            },
                            {
                                "content": "        'one bla bla two bla')",
                                "type": "added"
                            },
                            {
                                "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('Python', 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')",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 88,
                            "end_lineno": 107,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "eee1331f77ea7abafe78a7cffe6bf1b53fd0ca64^",
    "new_commit_hash": "eee1331f77ea7abafe78a7cffe6bf1b53fd0ca64",
    "commit_message": "ENH+TST: Generalize RST markup removal to any :role:",
    "commit_date": "2016-06-17T09:11:57+02:00",
    "metadata": {}
}
{
    "repo_name": "datalad",
    "new_commit_hash": "eee1331f77ea7abafe78a7cffe6bf1b53fd0ca64",
    "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    || Command line use only >>\n    Something for the cmdline only CMDONLY\n    Multiline!\n    << Command line use only ||\n\n    || Python use only >>\n\n    Some Python-only bits PYONLY\n    Multiline!\n\n    << Python use only ||\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      specify the dataset to perform the install operation on. If no\n      dataset is given, an attempt is made to identify the dataset based\n      on the current working directory and/or the `path` given.\n      Constraints: Value must be a Dataset or a valid identifier of a\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('Command line', alt)\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('Python', 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",
        "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.2.1.dev90\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 94ms\nInstalled 25 packages in 6.56s\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/.tmpj00iYH/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/.tmpj00iYH/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/.tmpj00iYH/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/.tmpj00iYH/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/.tmpj00iYH/lib/python3.9/site-packages/setuptools/build_meta.py\", line 320, in run_setup\n    exec(code, locals())\n  File \"<string>\", line 39, 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/.tmpSUshGt/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/.tmpSUshGt/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/.tmpSUshGt/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/.tmpSUshGt/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/.tmpSUshGt/lib/python3.9/site-packages/setuptools/build_meta.py\", line 320, in run_setup\n    exec(code, locals())\n  File \"<string>\", line 39, 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_eee1331f77ea7abafe78a7cffe6bf1b53fd0ca64/datalad/__init__.py\", line 20, in <module>\n    from .config import ConfigManager\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_eee1331f77ea7abafe78a7cffe6bf1b53fd0ca64/datalad/config.py\", line 17, in <module>\n    from appdirs import AppDirs\nModuleNotFoundError: No module named 'appdirs'\nUsing CPython 3.7.9\nCreating virtual environment at: .venv\nActivate with: source .venv/bin/activate\nResolved 26 packages in 228ms\nInstalled 26 packages in 5.70s\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 67 packages in 3.32s\nPrepared 1 package in 1.53s\nInstalled 61 packages in 44ms\n + appdirs==1.4.4\n + attrs==24.2.0\n + automat==22.10.0\n + beautifulsoup4==4.12.3\n + boto==2.49.0\n + certifi==2024.12.14\n + cffi==1.15.1\n + charset-normalizer==3.4.1\n + constantly==15.1.0\n + cryptography==44.0.0\n + cssselect==1.2.0\n + datalad==0.2.1.dev1 (from file:///home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_eee1331f77ea7abafe78a7cffe6bf1b53fd0ca64)\n + filelock==3.12.2\n + gitdb==4.0.12\n + gitpython==3.1.44\n + httpretty==1.1.4\n + humanize==4.6.0\n + hyperlink==21.0.0\n + idna==3.10\n + importlib-resources==5.12.0\n + incremental==22.10.0\n + itemadapter==0.8.0\n + itemloaders==1.1.0\n + jaraco-classes==3.2.3\n + jeepney==0.8.0\n + jmespath==1.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-python==0.5.6\n + multidict==6.0.5\n + nose==1.3.7\n + parsel==1.8.1\n + patool==1.12\n + protego==0.3.0\n + pyasn1==0.5.1\n + pyasn1-modules==0.3.0\n + pycparser==2.21\n + pydispatcher==2.0.7\n + pyopenssl==24.3.0\n + pyyaml==6.0.1\n + queuelib==1.6.2\n + requests==2.31.0\n + requests-file==2.1.0\n + scrapy==2.9.0\n + secretstorage==3.3.3\n + service-identity==21.1.0\n + smmap==5.0.2\n + soupsieve==2.4.1\n + testtools==2.7.1\n + tldextract==4.0.0\n + tqdm==4.67.1\n + twisted==23.8.0\n + urllib3==2.0.7\n + vcrpy==4.3.0\n + w3lib==2.1.2\n + wrapt==1.16.0\n + yarl==1.9.4\n + zope-interface==6.4.post2\nAudited 1 package in 13ms\n/home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_eee1331f77ea7abafe78a7cffe6bf1b53fd0ca64/.venv/lib/python3.7/site-packages/urllib3/__init__.py:38: NotOpenSSLWarning: urllib3 v2.0 only supports OpenSSL 1.1.1+, currently the 'ssl' module is compiled with 'LibreSSL 3.1.4'. See: https://github.com/urllib3/urllib3/issues/3020\n  exceptions.NotOpenSSLWarning,\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_eee1331f77ea7abafe78a7cffe6bf1b53fd0ca64\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 0.75s ===============================\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_eee1331f77ea7abafe78a7cffe6bf1b53fd0ca64\nplugins: cov-4.1.0\ncollected 3 items\n\nr2e_tests/test_1.py ..F                                                  [100%]\n\n=================================== FAILURES ===================================\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('Python', 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\nr2e_tests/test_1.py:100: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\na = ':murks:`me and my buddies`', b = 'me and my buddies', msg = None\n\n    def eq_(a, b, msg=None):\n        \"\"\"Shorthand for 'assert a == b, \"%r != %r\" % (a, b)\n        \"\"\"\n        if not a == b:\n>           raise AssertionError(msg or \"%r != %r\" % (a, b))\nE           AssertionError: ':murks:`me and my buddies`' != 'me and my buddies'\n\n.venv/lib/python3.7/site-packages/nose/tools/trivial.py:29: AssertionError\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\nFAILED r2e_tests/test_1.py::test_alter_interface_docs_for_cmdline - Assertion...\n========================= 1 failed, 2 passed in 0.49s ==========================\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**: RST Role Markup Not Removed for Custom Roles in Command-Line Docs

**Description**:
When using RST roles other than `:term:`, the markup is not removed correctly in command-line documentation. For example, processing a string with a custom role like `:murks:` leaves the role syntax intact, which affects the readability of the generated docs.

**Example Code**:
```python
docs = ':murks:`me and my buddies`'
processed_docs = alter_interface_docs_for_cmdline(docs)
print(processed_docs)
```

**Expected Behavior**:
The RST role markup should be removed, resulting in:
```
me and my buddies
```

**Actual Behavior**:
The RST role markup remains, resulting in:
```
:murks:`me and my buddies`
```

[/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 sys
import re

from ..ui import ui


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):
    import textwrap
    """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(
        '\|\| Command line use only \>\>.*\<\< Command line use only \|\|',
        '',
        docs,
        flags=re.MULTILINE | re.DOTALL)
    # clean cmdline in-line bits
    docs = re.sub(
        '\[CMD:\s.*CMD\]',
        '',
        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 use only \>\>.*\<\< Python use only \|\|',
        '',
        docs,
        flags=re.MULTILINE | re.DOTALL)
    # clean cmdline in-line bits
    docs = re.sub(
        '\[PY:\s.*PY\]',
        '',
        docs,
        flags=re.MULTILINE | re.DOTALL)
    # remove :term:`...` RST markup for cmdline docs
    docs = re.sub(
        r':term:`.*`[\\]*',
        lambda match: match.group(0)[7:].strip('`\\'),
        docs)
    # 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)
    return docs


def update_docstring_with_parameters(func, params, prefix=None, suffix=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 not defaults is 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 arg == 'self':
                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 not param.constraints is None:
                    param.constraints(defaults[defaults_idx])
            doc += param.get_autodoc(
                arg,
                default=defaults[defaults_idx] if defaults_idx >= 0 else None,
                has_default=defaults_idx >= 0)
            doc += '\n'
    doc += suffix if suffix else u""
    # assign the amended docs
    func.__doc__ = doc
    return func


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

    @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 arg == 'self':
                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 contraint 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
        argnames = getargspec(cls.__call__)[0]
        kwargs = {k: getattr(args, k) for k in argnames if k != 'self'}
        try:
            return cls.__call__(**kwargs)
        except KeyboardInterrupt:
            ui.error("\nInterrupted by user while doing magic")
            sys.exit(1)

```
--- 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**: RST Role Markup Not Removed for Custom Roles in Command-Line Docs

**Description**:
When using RST roles other than `:term:`, the markup is not removed correctly in command-line documentation. For example, processing a string with a custom role like `:murks:` leaves the role syntax intact, which affects the readability of the generated docs.

**Example Code**:
```python
docs = ':murks:`me and my buddies`'
processed_docs = alter_interface_docs_for_cmdline(docs)
print(processed_docs)
```

**Expected Behavior**:
The RST role markup should be removed, resulting in:
```
me and my buddies
```

**Actual Behavior**:
The RST role markup remains, resulting in:
```
:murks:`me and my buddies`
```

[/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 0acd42181..35aba6bed 100644
--- a/datalad/interface/base.py
+++ b/datalad/interface/base.py
@@ -110,11 +110,12 @@ def alter_interface_docs_for_cmdline(docs):
         '',
         docs,
         flags=re.MULTILINE | re.DOTALL)
-    # remove :term:`...` RST markup for cmdline docs
+    # remove :role:`...` RST markup for cmdline docs
     docs = re.sub(
-        r':term:`.*`[\\]*',
-        lambda match: match.group(0)[7:].strip('`\\'),
-        docs)
+        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

{"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 sys\nimport re\n\nfrom ..ui import ui\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    import textwrap\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        '\\|\\| Command line use only \\>\\>.*\\<\\< Command line use only \\|\\|',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # clean cmdline in-line bits\n    docs = re.sub(\n        '\\[CMD:\\s.*CMD\\]',\n        '',\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 use only \\>\\>.*\\<\\< Python use only \\|\\|',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # clean cmdline in-line bits\n    docs = re.sub(\n        '\\[PY:\\s.*PY\\]',\n        '',\n        docs,\n        flags=re.MULTILINE | re.DOTALL)\n    # remove :term:`...` RST markup for cmdline docs\n    docs = re.sub(\n        r':term:`.*`[\\\\]*',\n        lambda match: match.group(0)[7:].strip('`\\\\'),\n        docs)\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    return docs\n\n\ndef update_docstring_with_parameters(func, params, prefix=None, suffix=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 not defaults is 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 arg == 'self':\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 not param.constraints is None:\n                    param.constraints(defaults[defaults_idx])\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            doc += '\\n'\n    doc += suffix if suffix else u\"\"\n    # assign the amended docs\n    func.__doc__ = doc\n    return func\n\n\nclass Interface(object):\n    \"\"\"Base class for interface implementations\"\"\"\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 arg == 'self':\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 contraint 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        argnames = getargspec(cls.__call__)[0]\n        kwargs = {k: getattr(args, k) for k in argnames if k != 'self'}\n        try:\n            return cls.__call__(**kwargs)\n        except KeyboardInterrupt:\n            ui.error(\"\\nInterrupted by user while doing magic\")\n            sys.exit(1)\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**: RST Role Markup Not Removed for Custom Roles in Command-Line Docs

**Description**:
When using RST roles other than `:term:`, the markup is not removed correctly in command-line documentation. For example, processing a string with a custom role like `:murks:` leaves the role syntax intact, which affects the readability of the generated docs.

**Example Code**:
```python
docs = ':murks:`me and my buddies`'
processed_docs = alter_interface_docs_for_cmdline(docs)
print(processed_docs)
```

**Expected Behavior**:
The RST role markup should be removed, resulting in:
```
me and my buddies
```

**Actual Behavior**:
The RST role markup remains, resulting in:
```
:murks:`me and my buddies`
```

[/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 sys
import re

from ..ui import ui


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):
    import textwrap
    """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(
        '\|\| Command line use only \>\>.*\<\< Command line use only \|\|',
        '',
        docs,
        flags=re.MULTILINE | re.DOTALL)
    # clean cmdline in-line bits
    docs = re.sub(
        '\[CMD:\s.*CMD\]',
        '',
        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 use only \>\>.*\<\< Python use only \|\|',
        '',
        docs,
        flags=re.MULTILINE | re.DOTALL)
    # clean cmdline in-line bits
    docs = re.sub(
        '\[PY:\s.*PY\]',
        '',
        docs,
        flags=re.MULTILINE | re.DOTALL)
    # remove :term:`...` RST markup for cmdline docs
    docs = re.sub(
        r':term:`.*`[\\]*',
        lambda match: match.group(0)[7:].strip('`\\'),
        docs)
    # 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)
    return docs


def update_docstring_with_parameters(func, params, prefix=None, suffix=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 not defaults is 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 arg == 'self':
                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 not param.constraints is None:
                    param.constraints(defaults[defaults_idx])
            doc += param.get_autodoc(
                arg,
                default=defaults[defaults_idx] if defaults_idx >= 0 else None,
                has_default=defaults_idx >= 0)
            doc += '\n'
    doc += suffix if suffix else u""
    # assign the amended docs
    func.__doc__ = doc
    return func


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

    @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 arg == 'self':
                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 contraint 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
        argnames = getargspec(cls.__call__)[0]
        kwargs = {k: getattr(args, k) for k in argnames if k != 'self'}
        try:
            return cls.__call__(**kwargs)
        except KeyboardInterrupt:
            ui.error("\nInterrupted by user while doing magic")
            sys.exit(1)

```
--- 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