{
    "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\"\"\"Run prepared procedures (DataLad scripts) on a dataset\"\"\"\n\n__docformat__ = 'restructuredtext'\n\n\nimport logging\n\nfrom glob import iglob\nfrom argparse import REMAINDER\nimport os\nimport os.path as op\nimport stat\n\nfrom datalad import cfg\nfrom datalad.interface.base import Interface\nfrom datalad.interface.utils import eval_results\nfrom datalad.interface.base import build_doc\nfrom datalad.interface.results import get_status_dict\n\nfrom datalad.distribution.dataset import Dataset\nfrom datalad.distribution.dataset import require_dataset\nfrom datalad.distribution.dataset import EnsureDataset\nfrom datalad.support.constraints import EnsureNone\nfrom datalad.support.param import Parameter\nfrom datalad.distribution.dataset import datasetmethod\nfrom datalad.support.exceptions import InsufficientArgumentsError\nfrom datalad.support.exceptions import NoDatasetArgumentFound\n\nfrom datalad.utils import assure_list\n\n# bound dataset methods\nfrom datalad.interface.run import Run\nfrom datalad.distribution.subdatasets import Subdatasets\n\nlgr = logging.getLogger('datalad.interface.run_procedures')\n\n\ndef _get_file_match(dir, name='*'):\n    targets = (name, ('[!_]*.py'), ('[!_]*.sh'))\n    lgr.debug(\"Looking for procedure '%s' in '%s'\", name, dir)\n    for target in targets:\n        for m in iglob(op.join(dir, target)):\n            m_bn = op.basename(m)\n            if name == '*':\n                report_name = m_bn[:-3] if m_bn.endswith('.py') or \\\n                                           m_bn.endswith('.sh') \\\n                                        else m_bn\n                yield m, report_name\n            elif m_bn == name or m_bn.startswith('{}.'.format(name)):\n                yield m, name\n\n\ndef _get_proc_config(name, ds=None):\n    \"\"\"get configuration of named procedure\n\n    Returns\n    -------\n    tuple\n      (call format string, help string) or possibly None for either value,\n      if there's nothing configured\n    \"\"\"\n    # figure what ConfigManager to ask\n    cm = cfg if ds is None else ds.config\n    v = cm.get('datalad.procedures.{}.call-format'.format(name), None)\n    h = cm.get('datalad.procedures.{}.help'.format(name), None)\n    if isinstance(v, tuple):\n        # If multiple values are defined (at the same level), ConfigManager\n        # returns a tuple. Go with the last one.\n        # TODO: Should we issue a warning or fail entirely?\n        return v[-1], h\n    else:\n        return v, h\n\n\ndef _get_procedure_implementation(name='*', ds=None):\n    ds = ds if isinstance(ds, Dataset) else Dataset(ds) if ds else None\n\n    # 1. check system and user account for procedure\n    for loc in (cfg.obtain('datalad.locations.user-procedures'),\n                cfg.obtain('datalad.locations.system-procedures')):\n        for dir in assure_list(loc):\n            for m, n in _get_file_match(dir, name):\n                yield (m,) + _get_proc_config(n)\n    # 2. check dataset for procedure\n    if ds is not None and ds.is_installed():\n        # could be more than one\n        dirs = assure_list(\n                ds.config.obtain('datalad.locations.dataset-procedures'))\n        for dir in dirs:\n            # TODO `get` dirs if necessary\n            for m, n in _get_file_match(op.join(ds.path, dir), name):\n                yield (m,) + _get_proc_config(n, ds=ds)\n        # 1.1. check subdatasets recursively\n        for subds in ds.subdatasets(return_type='generator',\n                                    result_xfm='datasets'):\n            for m, f, h in _get_procedure_implementation(name=name, ds=subds):\n                yield m, f, h\n\n    # 3. check extensions for procedure\n    # delay heavy import until here\n    from pkg_resources import iter_entry_points\n    from pkg_resources import resource_isdir\n    from pkg_resources import resource_filename\n    for entry_point in iter_entry_points('datalad.extensions'):\n        # use of '/' here is OK wrt to platform compatibility\n        if resource_isdir(entry_point.module_name, 'resources/procedures'):\n            for m, n in _get_file_match(\n                    resource_filename(\n                        entry_point.module_name,\n                        'resources/procedures'),\n                    name):\n                yield (m,) + _get_proc_config(n)\n    # 4. at last check datalad itself for procedure\n    for m, n in _get_file_match(\n            resource_filename('datalad', 'resources/procedures'),\n            name):\n        yield (m,) + _get_proc_config(n)\n\n\ndef _guess_exec(script_file):\n    # TODO check for exec permission and rely on interpreter\n    if os.stat(script_file).st_mode & stat.S_IEXEC:\n        return ('executable', u'\"{script}\" \"{ds}\" {args}')\n    elif script_file.endswith('.sh'):\n        return (u'bash_script', u'bash \"{script}\" \"{ds}\" {args}')\n    elif script_file.endswith('.py'):\n        return (u'python_script', u'python \"{script}\" \"{ds}\" {args}')\n    else:\n        return None, None\n\n\n@build_doc\nclass RunProcedure(Interface):\n    \"\"\"Run prepared procedures (DataLad scripts) on a dataset\n\n    *Concept*\n\n    A \"procedure\" is an algorithm with the purpose to process a dataset in a\n    particular way. Procedures can be useful in a wide range of scenarios,\n    like adjusting dataset configuration in a uniform fashion, populating\n    a dataset with particular content, or automating other routine tasks,\n    such as synchronizing dataset content with certain siblings.\n\n    Implementations of some procedures are shipped together with DataLad,\n    but additional procedures can be provided by 1) any DataLad extension,\n    2) any dataset, 3) a local user, or 4) a local system administrator.\n    DataLad will look for procedures in the following locations and order:\n\n    Directories identified by the configuration settings\n\n    - 'datalad.locations.dataset-procedures'\n    - 'datalad.locations.user-procedures' (determined by\n      appdirs.user_config_dir; defaults to '$HOME/.config/datalad/procedures'\n      on GNU/Linux systems)\n    - 'datalad.locations.system-procedures' (determined by\n      appdirs.site_config_dir; defaults to '/etc/xdg/datalad/procedures' on\n      GNU/Linux systems)\n\n    and subsequently in the 'resources/procedures/' directories of any\n    installed extension, and, lastly, of the DataLad installation itself.\n\n    Each configuration setting can occur multiple times to indicate multiple\n    directories to be searched. If a procedure matching a given name is found\n    (filename without a possible extension), the search is aborted and this\n    implementation will be executed. This makes it possible for individual\n    datasets, users, or machines to override externally provided procedures\n    (enabling the implementation of customizable processing \"hooks\").\n\n\n    *Procedure implementation*\n\n    A procedure can be any executable. Executables must have the appropriate\n    permissions and, in the case of a script, must contain an appropriate\n    \"shebang\" line. If a procedure is not executable, but its filename ends\n    with '.py', it is automatically executed by the 'python' interpreter\n    (whichever version is available in the present environment). Likewise,\n    procedure implementations ending on '.sh' are executed via 'bash'.\n\n    Procedures can implement any argument handling, but must be capable\n    of taking at least one positional argument (the absolute path to the\n    dataset they shall operate on).\n\n\n    *Customize other commands with procedures*\n\n    On execution of any commands, DataLad inspects two additional\n    configuration settings:\n\n    - 'datalad.<name>.proc-pre'\n\n    - 'datalad.<name>.proc-post'\n\n    where '<name>' is the name of a DataLad command. Using this mechanism\n    DataLad can be instructed to run one or more procedures before or\n    after the execution of a given command. For example, configuring\n    a set of metadata types in any newly created dataset can be achieved\n    via:\n\n      % datalad -c 'datalad.create.proc-post=cfg_metadatatypes xmp image' create -d myds\n\n    As procedures run on datasets, it is necessary to explicitly identify\n    the target dataset via the -d (--dataset) option.\n    \"\"\"\n    _params_ = dict(\n        spec=Parameter(\n            args=(\"spec\",),\n            metavar='NAME [ARGS]',\n            nargs=REMAINDER,\n            doc=\"\"\"Name and possibly additional arguments of the\n            to-be-executed procedure.\"\"\"),\n        dataset=Parameter(\n            args=(\"-d\", \"--dataset\"),\n            metavar=\"PATH\",\n            doc=\"\"\"specify the dataset to run the procedure on.\n            An attempt is made to identify the dataset based on the current\n            working directory.\"\"\",\n            constraints=EnsureDataset() | EnsureNone()),\n        discover=Parameter(\n            args=('--discover',),\n            action='store_true',\n            doc=\"\"\"if given, all configured paths are searched for procedures\n            and one result record per discovered procedure is yielded, but\n            no procedure is executed\"\"\"),\n        help_proc=Parameter(\n            args=('--help-proc',),\n            action='store_true',\n            doc=\"\"\"if given, get a help message for procedure NAME from config\n            setting datalad.procedures.NAME.help. [CMD: Note, that this \n            parameter needs to be given before NAME, since it would be regarded \n            as part of ARGS otherwise CMD]\"\"\"\n        )\n    )\n\n    @staticmethod\n    @datasetmethod(name='run_procedure')\n    @eval_results\n    def __call__(\n            spec=None,\n            dataset=None,\n            discover=False,\n            help_proc=False):\n        if not spec and not discover:\n            raise InsufficientArgumentsError('requires at least a procedure name')\n        if help_proc and not spec:\n            raise InsufficientArgumentsError('requires a procedure name')\n\n        try:\n            ds = require_dataset(\n                dataset, check_installed=False,\n                purpose='run a procedure')\n        except NoDatasetArgumentFound:\n            ds = None\n\n        if discover:\n            reported = set()\n            for m, cmd_tmpl, cmd_help in _get_procedure_implementation('*', ds=ds):\n                if m in reported:\n                    continue\n                if not cmd_tmpl:\n                    cmd_type, cmd_tmpl = _guess_exec(m)\n                if cmd_type is None and cmd_tmpl is None:\n                    # doesn't seem like a match\n                    continue\n                res = get_status_dict(\n                    action='run_procedure',\n                    path=m,\n                    type='file',\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='ok',\n                    procedure_type=cmd_type,\n                    procedure_callfmt=cmd_tmpl,\n                    message=cmd_type)\n                reported.add(m)\n                yield res\n            return\n\n        if not isinstance(spec, (tuple, list)):\n            # maybe coming from config\n            import shlex\n            spec = shlex.split(spec)\n        name = spec[0]\n        args = spec[1:]\n\n        try:\n            # get the first match an run with it\n            procedure_file, cmd_tmpl, cmd_help = \\\n                next(_get_procedure_implementation(name, ds=ds))\n        except StopIteration:\n            # TODO error result\n            raise ValueError(\"Cannot find procedure with name '%s'\", name)\n\n        if not cmd_tmpl:\n            cmd_type, cmd_tmpl = _guess_exec(procedure_file)\n            if cmd_tmpl is None:\n                raise ValueError(\"No idea how to execute procedure %s. \"\n                                 \"Missing 'execute' permissions?\",\n                                 procedure_file)\n\n        if help_proc:\n            res = get_status_dict(\n                    action='procedure_help',\n                    path=procedure_file,\n                    type='file',\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='ok',\n                    procedure_callfmt=cmd_tmpl,\n                    message=cmd_help)\n            yield res\n            return\n\n        cmd = cmd_tmpl.format(\n            script=procedure_file,\n            ds=ds.path if ds else '',\n            args=u' '.join(u'\"{}\"'.format(a) for a in args) if args else '')\n        lgr.debug('Attempt to run procedure {} as: {}'.format(\n            name,\n            cmd))\n        for r in Run.__call__(\n                cmd=cmd,\n                dataset=ds,\n                explicit=True,\n                inputs=None,\n                outputs=None,\n                # pass through here\n                on_failure='ignore',\n        ):\n            yield r\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\"\"\"Run prepared procedures (DataLad scripts) on a dataset\"\"\"\n\n__docformat__ = 'restructuredtext'\n\n\nimport logging\n\nfrom glob import iglob\nfrom argparse import REMAINDER\nimport os\nimport os.path as op\nimport stat\n\nfrom datalad import cfg\nfrom datalad.interface.base import Interface\nfrom datalad.interface.utils import eval_results\nfrom datalad.interface.base import build_doc\nfrom datalad.interface.results import get_status_dict\n\nfrom datalad.distribution.dataset import Dataset\nfrom datalad.distribution.dataset import require_dataset\nfrom datalad.distribution.dataset import EnsureDataset\nfrom datalad.support.constraints import EnsureNone\nfrom datalad.support.param import Parameter\nfrom datalad.distribution.dataset import datasetmethod\nfrom datalad.support.exceptions import InsufficientArgumentsError\nfrom datalad.support.exceptions import NoDatasetArgumentFound\n\nfrom datalad.utils import assure_list\n\n# bound dataset methods\nfrom datalad.interface.run import Run\nfrom datalad.distribution.subdatasets import Subdatasets\n\nlgr = logging.getLogger('datalad.interface.run_procedures')\n\n\ndef _get_file_match(dir, name='*'):\n    targets = (name, ('[!_]*.py'), ('[!_]*.sh'))\n    lgr.debug(\"Looking for procedure '%s' in '%s'\", name, dir)\n    for target in targets:\n        for m in iglob(op.join(dir, target)):\n            m_bn = op.basename(m)\n            if name == '*':\n                report_name = m_bn[:-3] if m_bn.endswith('.py') or \\\n                                           m_bn.endswith('.sh') \\\n                                        else m_bn\n                yield m, report_name\n            elif m_bn == name or m_bn.startswith('{}.'.format(name)):\n                yield m, name\n\n\ndef _get_proc_config(name, ds=None):\n    \"\"\"get configuration of named procedure\n\n    Returns\n    -------\n    tuple\n      (call format string, help string) or possibly None for either value,\n      if there's nothing configured\n    \"\"\"\n    # figure what ConfigManager to ask\n    cm = cfg if ds is None else ds.config\n    v = cm.get('datalad.procedures.{}.call-format'.format(name), None)\n    h = cm.get('datalad.procedures.{}.help'.format(name), None)\n    if isinstance(v, tuple):\n        # If multiple values are defined (at the same level), ConfigManager\n        # returns a tuple. Go with the last one.\n        # TODO: Should we issue a warning or fail entirely?\n        return v[-1], h\n    else:\n        return v, h\n\n\ndef _get_procedure_implementation(name='*', ds=None):\n    ds = ds if isinstance(ds, Dataset) else Dataset(ds) if ds else None\n\n    # 1. check system and user account for procedure\n    for loc in (cfg.obtain('datalad.locations.user-procedures'),\n                cfg.obtain('datalad.locations.system-procedures')):\n        for dir in assure_list(loc):\n            for m, n in _get_file_match(dir, name):\n                yield (m,) + _get_proc_config(n)\n    # 2. check dataset for procedure\n    if ds is not None and ds.is_installed():\n        # could be more than one\n        dirs = assure_list(\n                ds.config.obtain('datalad.locations.dataset-procedures'))\n        for dir in dirs:\n            # TODO `get` dirs if necessary\n            for m, n in _get_file_match(op.join(ds.path, dir), name):\n                yield (m,) + _get_proc_config(n, ds=ds)\n        # 1.1. check subdatasets recursively\n        for subds in ds.subdatasets(return_type='generator',\n                                    result_xfm='datasets'):\n            for m, f, h in _get_procedure_implementation(name=name, ds=subds):\n                yield m, f, h\n\n    # 3. check extensions for procedure\n    # delay heavy import until here\n    from pkg_resources import iter_entry_points\n    from pkg_resources import resource_isdir\n    from pkg_resources import resource_filename\n    for entry_point in iter_entry_points('datalad.extensions'):\n        # use of '/' here is OK wrt to platform compatibility\n        if resource_isdir(entry_point.module_name, 'resources/procedures'):\n            for m, n in _get_file_match(\n                    resource_filename(\n                        entry_point.module_name,\n                        'resources/procedures'),\n                    name):\n                yield (m,) + _get_proc_config(n)\n    # 4. at last check datalad itself for procedure\n    for m, n in _get_file_match(\n            resource_filename('datalad', 'resources/procedures'),\n            name):\n        yield (m,) + _get_proc_config(n)\n\n\ndef _guess_exec(script_file):\n    # TODO check for exec permission and rely on interpreter\n    if os.stat(script_file).st_mode & stat.S_IEXEC:\n        return ('executable', u'\"{script}\" \"{ds}\" {args}')\n    elif script_file.endswith('.sh'):\n        return (u'bash_script', u'bash \"{script}\" \"{ds}\" {args}')\n    elif script_file.endswith('.py'):\n        return (u'python_script', u'python \"{script}\" \"{ds}\" {args}')\n    else:\n        return None, None\n\n\n@build_doc\nclass RunProcedure(Interface):\n    \"\"\"Run prepared procedures (DataLad scripts) on a dataset\n\n    *Concept*\n\n    A \"procedure\" is an algorithm with the purpose to process a dataset in a\n    particular way. Procedures can be useful in a wide range of scenarios,\n    like adjusting dataset configuration in a uniform fashion, populating\n    a dataset with particular content, or automating other routine tasks,\n    such as synchronizing dataset content with certain siblings.\n\n    Implementations of some procedures are shipped together with DataLad,\n    but additional procedures can be provided by 1) any DataLad extension,\n    2) any dataset, 3) a local user, or 4) a local system administrator.\n    DataLad will look for procedures in the following locations and order:\n\n    Directories identified by the configuration settings\n\n    - 'datalad.locations.dataset-procedures'\n    - 'datalad.locations.user-procedures' (determined by\n      appdirs.user_config_dir; defaults to '$HOME/.config/datalad/procedures'\n      on GNU/Linux systems)\n    - 'datalad.locations.system-procedures' (determined by\n      appdirs.site_config_dir; defaults to '/etc/xdg/datalad/procedures' on\n      GNU/Linux systems)\n\n    and subsequently in the 'resources/procedures/' directories of any\n    installed extension, and, lastly, of the DataLad installation itself.\n\n    Each configuration setting can occur multiple times to indicate multiple\n    directories to be searched. If a procedure matching a given name is found\n    (filename without a possible extension), the search is aborted and this\n    implementation will be executed. This makes it possible for individual\n    datasets, users, or machines to override externally provided procedures\n    (enabling the implementation of customizable processing \"hooks\").\n\n\n    *Procedure implementation*\n\n    A procedure can be any executable. Executables must have the appropriate\n    permissions and, in the case of a script, must contain an appropriate\n    \"shebang\" line. If a procedure is not executable, but its filename ends\n    with '.py', it is automatically executed by the 'python' interpreter\n    (whichever version is available in the present environment). Likewise,\n    procedure implementations ending on '.sh' are executed via 'bash'.\n\n    Procedures can implement any argument handling, but must be capable\n    of taking at least one positional argument (the absolute path to the\n    dataset they shall operate on).\n\n\n    *Customize other commands with procedures*\n\n    On execution of any commands, DataLad inspects two additional\n    configuration settings:\n\n    - 'datalad.<name>.proc-pre'\n\n    - 'datalad.<name>.proc-post'\n\n    where '<name>' is the name of a DataLad command. Using this mechanism\n    DataLad can be instructed to run one or more procedures before or\n    after the execution of a given command. For example, configuring\n    a set of metadata types in any newly created dataset can be achieved\n    via:\n\n      % datalad -c 'datalad.create.proc-post=cfg_metadatatypes xmp image' create -d myds\n\n    As procedures run on datasets, it is necessary to explicitly identify\n    the target dataset via the -d (--dataset) option.\n    \"\"\"\n    _params_ = dict(\n        spec=Parameter(\n            args=(\"spec\",),\n            metavar='NAME [ARGS]',\n            nargs=REMAINDER,\n            doc=\"\"\"Name and possibly additional arguments of the\n            to-be-executed procedure.\"\"\"),\n        dataset=Parameter(\n            args=(\"-d\", \"--dataset\"),\n            metavar=\"PATH\",\n            doc=\"\"\"specify the dataset to run the procedure on.\n            An attempt is made to identify the dataset based on the current\n            working directory.\"\"\",\n            constraints=EnsureDataset() | EnsureNone()),\n        discover=Parameter(\n            args=('--discover',),\n            action='store_true',\n            doc=\"\"\"if given, all configured paths are searched for procedures\n            and one result record per discovered procedure is yielded, but\n            no procedure is executed\"\"\"),\n        help_proc=Parameter(\n            args=('--help-proc',),\n            action='store_true',\n            doc=\"\"\"if given, get a help message for procedure NAME from config\n            setting datalad.procedures.NAME.help. [CMD: Note, that this \n            parameter needs to be given before NAME, since it would be regarded \n            as part of ARGS otherwise CMD]\"\"\"\n        )\n    )\n\n    @staticmethod\n    @datasetmethod(name='run_procedure')\n    @eval_results\n    def __call__(\n            spec=None,\n            dataset=None,\n            discover=False,\n            help_proc=False):\n        if not spec and not discover:\n            raise InsufficientArgumentsError('requires at least a procedure name')\n        if help_proc and not spec:\n            raise InsufficientArgumentsError('requires a procedure name')\n\n        try:\n            ds = require_dataset(\n                dataset, check_installed=False,\n                purpose='run a procedure')\n        except NoDatasetArgumentFound:\n            ds = None\n\n        if discover:\n            reported = set()\n            for m, cmd_tmpl, cmd_help in _get_procedure_implementation('*', ds=ds):\n                if m in reported:\n                    continue\n                cmd_type, cmd_tmpl_g = _guess_exec(m)\n                if not cmd_tmpl:\n                    cmd_tmpl = cmd_tmpl_g\n                if cmd_type is None and cmd_tmpl is None:\n                    # doesn't seem like a match\n                    continue\n                res = get_status_dict(\n                    action='run_procedure',\n                    path=m,\n                    type='file',\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='ok',\n                    procedure_type=cmd_type,\n                    procedure_callfmt=cmd_tmpl,\n                    message=cmd_type)\n                reported.add(m)\n                yield res\n            return\n\n        if not isinstance(spec, (tuple, list)):\n            # maybe coming from config\n            import shlex\n            spec = shlex.split(spec)\n        name = spec[0]\n        args = spec[1:]\n\n        try:\n            # get the first match an run with it\n            procedure_file, cmd_tmpl, cmd_help = \\\n                next(_get_procedure_implementation(name, ds=ds))\n        except StopIteration:\n            res = get_status_dict(\n                    action='run_procedure',\n                    # TODO: Default renderer requires a key \"path\" to exist.\n                    # Doesn't make a lot of sense in this case\n                    path=name,\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='impossible',\n                    message=\"Cannot find procedure with name '%s'\" % name)\n            yield res\n            return\n\n        cmd_type, cmd_tmpl_g = _guess_exec(procedure_file)\n        if not cmd_tmpl:\n            cmd_tmpl = cmd_tmpl_g\n        if not cmd_tmpl:\n            raise ValueError(\"No idea how to execute procedure %s. \"\n                             \"Missing 'execute' permissions?\",\n                             procedure_file)\n\n        if help_proc:\n            res = get_status_dict(\n                    action='procedure_help',\n                    path=procedure_file,\n                    type='file',\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='ok',\n                    procedure_type=cmd_type,\n                    procedure_callfmt=cmd_tmpl,\n                    message=cmd_help)\n            yield res\n            return\n\n        cmd = cmd_tmpl.format(\n            script=procedure_file,\n            ds=ds.path if ds else '',\n            args=u' '.join(u'\"{}\"'.format(a) for a in args) if args else '')\n        lgr.debug('Attempt to run procedure {} as: {}'.format(\n            name,\n            cmd))\n        for r in Run.__call__(\n                cmd=cmd,\n                dataset=ds,\n                explicit=True,\n                inputs=None,\n                outputs=None,\n                # pass through here\n                on_failure='ignore',\n        ):\n            yield r\n",
            "header": {
                "file": {
                    "path": "datalad/interface/run_procedure.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "1ea343927",
                "new_commit_hash": "6ed01fa1f",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/datalad/interface/run_procedure.py"
            },
            "plus_file": {
                "path": "b/datalad/interface/run_procedure.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 264,
                            "length": 8
                        },
                        "new_range": {
                            "start": 264,
                            "length": 9
                        },
                        "section": "class RunProcedure(Interface):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "            for m, cmd_tmpl, cmd_help in _get_procedure_implementation('*', ds=ds):",
                                "type": "context"
                            },
                            {
                                "content": "                if m in reported:",
                                "type": "context"
                            },
                            {
                                "content": "                    continue",
                                "type": "context"
                            },
                            {
                                "content": "                cmd_type, cmd_tmpl_g = _guess_exec(m)",
                                "type": "added"
                            },
                            {
                                "content": "                if not cmd_tmpl:",
                                "type": "context"
                            },
                            {
                                "content": "                    cmd_type, cmd_tmpl = _guess_exec(m)",
                                "type": "deleted"
                            },
                            {
                                "content": "                    cmd_tmpl = cmd_tmpl_g",
                                "type": "added"
                            },
                            {
                                "content": "                if cmd_type is None and cmd_tmpl is None:",
                                "type": "context"
                            },
                            {
                                "content": "                    # doesn't seem like a match",
                                "type": "context"
                            },
                            {
                                "content": "                    continue",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "datalad/interface/run_procedure.py",
                            "type": "class",
                            "name": "RunProcedure",
                            "content": "class RunProcedure(Interface):\n    \"\"\"Run prepared procedures (DataLad scripts) on a dataset\n\n    *Concept*\n\n    A \"procedure\" is an algorithm with the purpose to process a dataset in a\n    particular way. Procedures can be useful in a wide range of scenarios,\n    like adjusting dataset configuration in a uniform fashion, populating\n    a dataset with particular content, or automating other routine tasks,\n    such as synchronizing dataset content with certain siblings.\n\n    Implementations of some procedures are shipped together with DataLad,\n    but additional procedures can be provided by 1) any DataLad extension,\n    2) any dataset, 3) a local user, or 4) a local system administrator.\n    DataLad will look for procedures in the following locations and order:\n\n    Directories identified by the configuration settings\n\n    - 'datalad.locations.dataset-procedures'\n    - 'datalad.locations.user-procedures' (determined by\n      appdirs.user_config_dir; defaults to '$HOME/.config/datalad/procedures'\n      on GNU/Linux systems)\n    - 'datalad.locations.system-procedures' (determined by\n      appdirs.site_config_dir; defaults to '/etc/xdg/datalad/procedures' on\n      GNU/Linux systems)\n\n    and subsequently in the 'resources/procedures/' directories of any\n    installed extension, and, lastly, of the DataLad installation itself.\n\n    Each configuration setting can occur multiple times to indicate multiple\n    directories to be searched. If a procedure matching a given name is found\n    (filename without a possible extension), the search is aborted and this\n    implementation will be executed. This makes it possible for individual\n    datasets, users, or machines to override externally provided procedures\n    (enabling the implementation of customizable processing \"hooks\").\n\n\n    *Procedure implementation*\n\n    A procedure can be any executable. Executables must have the appropriate\n    permissions and, in the case of a script, must contain an appropriate\n    \"shebang\" line. If a procedure is not executable, but its filename ends\n    with '.py', it is automatically executed by the 'python' interpreter\n    (whichever version is available in the present environment). Likewise,\n    procedure implementations ending on '.sh' are executed via 'bash'.\n\n    Procedures can implement any argument handling, but must be capable\n    of taking at least one positional argument (the absolute path to the\n    dataset they shall operate on).\n\n\n    *Customize other commands with procedures*\n\n    On execution of any commands, DataLad inspects two additional\n    configuration settings:\n\n    - 'datalad.<name>.proc-pre'\n\n    - 'datalad.<name>.proc-post'\n\n    where '<name>' is the name of a DataLad command. Using this mechanism\n    DataLad can be instructed to run one or more procedures before or\n    after the execution of a given command. For example, configuring\n    a set of metadata types in any newly created dataset can be achieved\n    via:\n\n      % datalad -c 'datalad.create.proc-post=cfg_metadatatypes xmp image' create -d myds\n\n    As procedures run on datasets, it is necessary to explicitly identify\n    the target dataset via the -d (--dataset) option.\n    \"\"\"\n    _params_ = dict(\n        spec=Parameter(\n            args=(\"spec\",),\n            metavar='NAME [ARGS]',\n            nargs=REMAINDER,\n            doc=\"\"\"Name and possibly additional arguments of the\n            to-be-executed procedure.\"\"\"),\n        dataset=Parameter(\n            args=(\"-d\", \"--dataset\"),\n            metavar=\"PATH\",\n            doc=\"\"\"specify the dataset to run the procedure on.\n            An attempt is made to identify the dataset based on the current\n            working directory.\"\"\",\n            constraints=EnsureDataset() | EnsureNone()),\n        discover=Parameter(\n            args=('--discover',),\n            action='store_true',\n            doc=\"\"\"if given, all configured paths are searched for procedures\n            and one result record per discovered procedure is yielded, but\n            no procedure is executed\"\"\"),\n        help_proc=Parameter(\n            args=('--help-proc',),\n            action='store_true',\n            doc=\"\"\"if given, get a help message for procedure NAME from config\n            setting datalad.procedures.NAME.help. [CMD: Note, that this \n            parameter needs to be given before NAME, since it would be regarded \n            as part of ARGS otherwise CMD]\"\"\"\n        )\n    )\n\n    @staticmethod\n    @datasetmethod(name='run_procedure')\n    @eval_results\n    def __call__(\n            spec=None,\n            dataset=None,\n            discover=False,\n            help_proc=False):\n        if not spec and not discover:\n            raise InsufficientArgumentsError('requires at least a procedure name')\n        if help_proc and not spec:\n            raise InsufficientArgumentsError('requires a procedure name')\n\n        try:\n            ds = require_dataset(\n                dataset, check_installed=False,\n                purpose='run a procedure')\n        except NoDatasetArgumentFound:\n            ds = None\n\n        if discover:\n            reported = set()\n            for m, cmd_tmpl, cmd_help in _get_procedure_implementation('*', ds=ds):\n                if m in reported:\n                    continue\n                if not cmd_tmpl:\n                    cmd_type, cmd_tmpl = _guess_exec(m)\n                if cmd_type is None and cmd_tmpl is None:\n                    # doesn't seem like a match\n                    continue\n                res = get_status_dict(\n                    action='run_procedure',\n                    path=m,\n                    type='file',\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='ok',\n                    procedure_type=cmd_type,\n                    procedure_callfmt=cmd_tmpl,\n                    message=cmd_type)\n                reported.add(m)\n                yield res\n            return\n\n        if not isinstance(spec, (tuple, list)):\n            # maybe coming from config\n            import shlex\n            spec = shlex.split(spec)\n        name = spec[0]\n        args = spec[1:]\n\n        try:\n            # get the first match an run with it\n            procedure_file, cmd_tmpl, cmd_help = \\\n                next(_get_procedure_implementation(name, ds=ds))\n        except StopIteration:\n            # TODO error result\n            raise ValueError(\"Cannot find procedure with name '%s'\", name)\n\n        if not cmd_tmpl:\n            cmd_type, cmd_tmpl = _guess_exec(procedure_file)\n            if cmd_tmpl is None:\n                raise ValueError(\"No idea how to execute procedure %s. \"\n                                 \"Missing 'execute' permissions?\",\n                                 procedure_file)\n\n        if help_proc:\n            res = get_status_dict(\n                    action='procedure_help',\n                    path=procedure_file,\n                    type='file',\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='ok',\n                    procedure_callfmt=cmd_tmpl,\n                    message=cmd_help)\n            yield res\n            return\n\n        cmd = cmd_tmpl.format(\n            script=procedure_file,\n            ds=ds.path if ds else '',\n            args=u' '.join(u'\"{}\"'.format(a) for a in args) if args else '')\n        lgr.debug('Attempt to run procedure {} as: {}'.format(\n            name,\n            cmd))\n        for r in Run.__call__(\n                cmd=cmd,\n                dataset=ds,\n                explicit=True,\n                inputs=None,\n                outputs=None,\n                # pass through here\n                on_failure='ignore',\n        ):\n            yield r",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 141,
                            "end_lineno": 337,
                            "parent": null
                        },
                        {
                            "file_name": "datalad/interface/run_procedure.py",
                            "type": "method",
                            "name": "RunProcedure.__call__",
                            "content": "class RunProcedure(Interface):\n    \"\"\"Run prepared procedures (DataLad scripts) on a dataset\n\n    *Concept*\n\n    A \"procedure\" is an algorithm with the purpose to process a dataset in a\n    particular way. Procedures can be useful in a wide range of scenarios,\n    like adjusting dataset configuration in a uniform fashion, populating\n    a dataset with particular content, or automating other routine tasks,\n    such as synchronizing dataset content with certain siblings.\n\n    Implementations of some procedures are shipped together with DataLad,\n    but additional procedures can be provided by 1) any DataLad extension,\n    2) any dataset, 3) a local user, or 4) a local system administrator.\n    DataLad will look for procedures in the following locations and order:\n\n    Directories identified by the configuration settings\n\n    - 'datalad.locations.dataset-procedures'\n    - 'datalad.locations.user-procedures' (determined by\n      appdirs.user_config_dir; defaults to '$HOME/.config/datalad/procedures'\n      on GNU/Linux systems)\n    - 'datalad.locations.system-procedures' (determined by\n      appdirs.site_config_dir; defaults to '/etc/xdg/datalad/procedures' on\n      GNU/Linux systems)\n\n    and subsequently in the 'resources/procedures/' directories of any\n    installed extension, and, lastly, of the DataLad installation itself.\n\n    Each configuration setting can occur multiple times to indicate multiple\n    directories to be searched. If a procedure matching a given name is found\n    (filename without a possible extension), the search is aborted and this\n    implementation will be executed. This makes it possible for individual\n    datasets, users, or machines to override externally provided procedures\n    (enabling the implementation of customizable processing \"hooks\").\n\n\n    *Procedure implementation*\n\n    A procedure can be any executable. Executables must have the appropriate\n    permissions and, in the case of a script, must contain an appropriate\n    \"shebang\" line. If a procedure is not executable, but its filename ends\n    with '.py', it is automatically executed by the 'python' interpreter\n    (whichever version is available in the present environment). Likewise,\n    procedure implementations ending on '.sh' are executed via 'bash'.\n\n    Procedures can implement any argument handling, but must be capable\n    of taking at least one positional argument (the absolute path to the\n    dataset they shall operate on).\n\n\n    *Customize other commands with procedures*\n\n    On execution of any commands, DataLad inspects two additional\n    configuration settings:\n\n    - 'datalad.<name>.proc-pre'\n\n    - 'datalad.<name>.proc-post'\n\n    where '<name>' is the name of a DataLad command. Using this mechanism\n    DataLad can be instructed to run one or more procedures before or\n    after the execution of a given command. For example, configuring\n    a set of metadata types in any newly created dataset can be achieved\n    via:\n\n      % datalad -c 'datalad.create.proc-post=cfg_metadatatypes xmp image' create -d myds\n\n    As procedures run on datasets, it is necessary to explicitly identify\n    the target dataset via the -d (--dataset) option.\n    \"\"\"\n    _params_ = dict(\n        spec=Parameter(\n            args=(\"spec\",),\n            metavar='NAME [ARGS]',\n            nargs=REMAINDER,\n            doc=\"\"\"Name and possibly additional arguments of the\n            to-be-executed procedure.\"\"\"),\n        dataset=Parameter(\n            args=(\"-d\", \"--dataset\"),\n            metavar=\"PATH\",\n            doc=\"\"\"specify the dataset to run the procedure on.\n            An attempt is made to identify the dataset based on the current\n            working directory.\"\"\",\n            constraints=EnsureDataset() | EnsureNone()),\n        discover=Parameter(\n            args=('--discover',),\n            action='store_true',\n            doc=\"\"\"if given, all configured paths are searched for procedures\n            and one result record per discovered procedure is yielded, but\n            no procedure is executed\"\"\"),\n        help_proc=Parameter(\n            args=('--help-proc',),\n            action='store_true',\n            doc=\"\"\"if given, get a help message for procedure NAME from config\n            setting datalad.procedures.NAME.help. [CMD: Note, that this \n            parameter needs to be given before NAME, since it would be regarded \n            as part of ARGS otherwise CMD]\"\"\"\n        )\n    )\n\n    @staticmethod\n    @datasetmethod(name='run_procedure')\n    @eval_results\n    def __call__(\n            spec=None,\n            dataset=None,\n            discover=False,\n            help_proc=False):\n        if not spec and not discover:\n            raise InsufficientArgumentsError('requires at least a procedure name')\n        if help_proc and not spec:\n            raise InsufficientArgumentsError('requires a procedure name')\n\n        try:\n            ds = require_dataset(\n                dataset, check_installed=False,\n                purpose='run a procedure')\n        except NoDatasetArgumentFound:\n            ds = None\n\n        if discover:\n            reported = set()\n            for m, cmd_tmpl, cmd_help in _get_procedure_implementation('*', ds=ds):\n                if m in reported:\n                    continue\n                if not cmd_tmpl:\n                    cmd_type, cmd_tmpl = _guess_exec(m)\n                if cmd_type is None and cmd_tmpl is None:\n                    # doesn't seem like a match\n                    continue\n                res = get_status_dict(\n                    action='run_procedure',\n                    path=m,\n                    type='file',\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='ok',\n                    procedure_type=cmd_type,\n                    procedure_callfmt=cmd_tmpl,\n                    message=cmd_type)\n                reported.add(m)\n                yield res\n            return\n\n        if not isinstance(spec, (tuple, list)):\n            # maybe coming from config\n            import shlex\n            spec = shlex.split(spec)\n        name = spec[0]\n        args = spec[1:]\n\n        try:\n            # get the first match an run with it\n            procedure_file, cmd_tmpl, cmd_help = \\\n                next(_get_procedure_implementation(name, ds=ds))\n        except StopIteration:\n            # TODO error result\n            raise ValueError(\"Cannot find procedure with name '%s'\", name)\n\n        if not cmd_tmpl:\n            cmd_type, cmd_tmpl = _guess_exec(procedure_file)\n            if cmd_tmpl is None:\n                raise ValueError(\"No idea how to execute procedure %s. \"\n                                 \"Missing 'execute' permissions?\",\n                                 procedure_file)\n\n        if help_proc:\n            res = get_status_dict(\n                    action='procedure_help',\n                    path=procedure_file,\n                    type='file',\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='ok',\n                    procedure_callfmt=cmd_tmpl,\n                    message=cmd_help)\n            yield res\n            return\n\n        cmd = cmd_tmpl.format(\n            script=procedure_file,\n            ds=ds.path if ds else '',\n            args=u' '.join(u'\"{}\"'.format(a) for a in args) if args else '')\n        lgr.debug('Attempt to run procedure {} as: {}'.format(\n            name,\n            cmd))\n        for r in Run.__call__(\n                cmd=cmd,\n                dataset=ds,\n                explicit=True,\n                inputs=None,\n                outputs=None,\n                # pass through here\n                on_failure='ignore',\n        ):\n            yield r",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 245,
                            "end_lineno": 337,
                            "parent": {
                                "file_name": "datalad/interface/run_procedure.py",
                                "type": "class",
                                "name": "RunProcedure",
                                "content": "class RunProcedure(Interface):\n    \"\"\"Run prepared procedures (DataLad scripts) on a dataset\n\n    *Concept*\n\n    A \"procedure\" is an algorithm with the purpose to process a dataset in a\n    particular way. Procedures can be useful in a wide range of scenarios,\n    like adjusting dataset configuration in a uniform fashion, populating\n    a dataset with particular content, or automating other routine tasks,\n    such as synchronizing dataset content with certain siblings.\n\n    Implementations of some procedures are shipped together with DataLad,\n    but additional procedures can be provided by 1) any DataLad extension,\n    2) any dataset, 3) a local user, or 4) a local system administrator.\n    DataLad will look for procedures in the following locations and order:\n\n    Directories identified by the configuration settings\n\n    - 'datalad.locations.dataset-procedures'\n    - 'datalad.locations.user-procedures' (determined by\n      appdirs.user_config_dir; defaults to '$HOME/.config/datalad/procedures'\n      on GNU/Linux systems)\n    - 'datalad.locations.system-procedures' (determined by\n      appdirs.site_config_dir; defaults to '/etc/xdg/datalad/procedures' on\n      GNU/Linux systems)\n\n    and subsequently in the 'resources/procedures/' directories of any\n    installed extension, and, lastly, of the DataLad installation itself.\n\n    Each configuration setting can occur multiple times to indicate multiple\n    directories to be searched. If a procedure matching a given name is found\n    (filename without a possible extension), the search is aborted and this\n    implementation will be executed. This makes it possible for individual\n    datasets, users, or machines to override externally provided procedures\n    (enabling the implementation of customizable processing \"hooks\").\n\n\n    *Procedure implementation*\n\n    A procedure can be any executable. Executables must have the appropriate\n    permissions and, in the case of a script, must contain an appropriate\n    \"shebang\" line. If a procedure is not executable, but its filename ends\n    with '.py', it is automatically executed by the 'python' interpreter\n    (whichever version is available in the present environment). Likewise,\n    procedure implementations ending on '.sh' are executed via 'bash'.\n\n    Procedures can implement any argument handling, but must be capable\n    of taking at least one positional argument (the absolute path to the\n    dataset they shall operate on).\n\n\n    *Customize other commands with procedures*\n\n    On execution of any commands, DataLad inspects two additional\n    configuration settings:\n\n    - 'datalad.<name>.proc-pre'\n\n    - 'datalad.<name>.proc-post'\n\n    where '<name>' is the name of a DataLad command. Using this mechanism\n    DataLad can be instructed to run one or more procedures before or\n    after the execution of a given command. For example, configuring\n    a set of metadata types in any newly created dataset can be achieved\n    via:\n\n      % datalad -c 'datalad.create.proc-post=cfg_metadatatypes xmp image' create -d myds\n\n    As procedures run on datasets, it is necessary to explicitly identify\n    the target dataset via the -d (--dataset) option.\n    \"\"\"\n    _params_ = dict(\n        spec=Parameter(\n            args=(\"spec\",),\n            metavar='NAME [ARGS]',\n            nargs=REMAINDER,\n            doc=\"\"\"Name and possibly additional arguments of the\n            to-be-executed procedure.\"\"\"),\n        dataset=Parameter(\n            args=(\"-d\", \"--dataset\"),\n            metavar=\"PATH\",\n            doc=\"\"\"specify the dataset to run the procedure on.\n            An attempt is made to identify the dataset based on the current\n            working directory.\"\"\",\n            constraints=EnsureDataset() | EnsureNone()),\n        discover=Parameter(\n            args=('--discover',),\n            action='store_true',\n            doc=\"\"\"if given, all configured paths are searched for procedures\n            and one result record per discovered procedure is yielded, but\n            no procedure is executed\"\"\"),\n        help_proc=Parameter(\n            args=('--help-proc',),\n            action='store_true',\n            doc=\"\"\"if given, get a help message for procedure NAME from config\n            setting datalad.procedures.NAME.help. [CMD: Note, that this \n            parameter needs to be given before NAME, since it would be regarded \n            as part of ARGS otherwise CMD]\"\"\"\n        )\n    )\n\n    @staticmethod\n    @datasetmethod(name='run_procedure')\n    @eval_results\n    def __call__(\n            spec=None,\n            dataset=None,\n            discover=False,\n            help_proc=False):\n        if not spec and not discover:\n            raise InsufficientArgumentsError('requires at least a procedure name')\n        if help_proc and not spec:\n            raise InsufficientArgumentsError('requires a procedure name')\n\n        try:\n            ds = require_dataset(\n                dataset, check_installed=False,\n                purpose='run a procedure')\n        except NoDatasetArgumentFound:\n            ds = None\n\n        if discover:\n            reported = set()\n            for m, cmd_tmpl, cmd_help in _get_procedure_implementation('*', ds=ds):\n                if m in reported:\n                    continue\n                if not cmd_tmpl:\n                    cmd_type, cmd_tmpl = _guess_exec(m)\n                if cmd_type is None and cmd_tmpl is None:\n                    # doesn't seem like a match\n                    continue\n                res = get_status_dict(\n                    action='run_procedure',\n                    path=m,\n                    type='file',\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='ok',\n                    procedure_type=cmd_type,\n                    procedure_callfmt=cmd_tmpl,\n                    message=cmd_type)\n                reported.add(m)\n                yield res\n            return\n\n        if not isinstance(spec, (tuple, list)):\n            # maybe coming from config\n            import shlex\n            spec = shlex.split(spec)\n        name = spec[0]\n        args = spec[1:]\n\n        try:\n            # get the first match an run with it\n            procedure_file, cmd_tmpl, cmd_help = \\\n                next(_get_procedure_implementation(name, ds=ds))\n        except StopIteration:\n            # TODO error result\n            raise ValueError(\"Cannot find procedure with name '%s'\", name)\n\n        if not cmd_tmpl:\n            cmd_type, cmd_tmpl = _guess_exec(procedure_file)\n            if cmd_tmpl is None:\n                raise ValueError(\"No idea how to execute procedure %s. \"\n                                 \"Missing 'execute' permissions?\",\n                                 procedure_file)\n\n        if help_proc:\n            res = get_status_dict(\n                    action='procedure_help',\n                    path=procedure_file,\n                    type='file',\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='ok',\n                    procedure_callfmt=cmd_tmpl,\n                    message=cmd_help)\n            yield res\n            return\n\n        cmd = cmd_tmpl.format(\n            script=procedure_file,\n            ds=ds.path if ds else '',\n            args=u' '.join(u'\"{}\"'.format(a) for a in args) if args else '')\n        lgr.debug('Attempt to run procedure {} as: {}'.format(\n            name,\n            cmd))\n        for r in Run.__call__(\n                cmd=cmd,\n                dataset=ds,\n                explicit=True,\n                inputs=None,\n                outputs=None,\n                # pass through here\n                on_failure='ignore',\n        ):\n            yield r",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 141,
                                "end_lineno": 337,
                                "parent": null
                            }
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 295,
                            "length": 15
                        },
                        "new_range": {
                            "start": 296,
                            "length": 25
                        },
                        "section": "class RunProcedure(Interface):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "            procedure_file, cmd_tmpl, cmd_help = \\",
                                "type": "context"
                            },
                            {
                                "content": "                next(_get_procedure_implementation(name, ds=ds))",
                                "type": "context"
                            },
                            {
                                "content": "        except StopIteration:",
                                "type": "context"
                            },
                            {
                                "content": "            # TODO error result",
                                "type": "deleted"
                            },
                            {
                                "content": "            raise ValueError(\"Cannot find procedure with name '%s'\", name)",
                                "type": "deleted"
                            },
                            {
                                "content": "            res = get_status_dict(",
                                "type": "added"
                            },
                            {
                                "content": "                    action='run_procedure',",
                                "type": "added"
                            },
                            {
                                "content": "                    # TODO: Default renderer requires a key \"path\" to exist.",
                                "type": "added"
                            },
                            {
                                "content": "                    # Doesn't make a lot of sense in this case",
                                "type": "added"
                            },
                            {
                                "content": "                    path=name,",
                                "type": "added"
                            },
                            {
                                "content": "                    logger=lgr,",
                                "type": "added"
                            },
                            {
                                "content": "                    refds=ds.path if ds else None,",
                                "type": "added"
                            },
                            {
                                "content": "                    status='impossible',",
                                "type": "added"
                            },
                            {
                                "content": "                    message=\"Cannot find procedure with name '%s'\" % name)",
                                "type": "added"
                            },
                            {
                                "content": "            yield res",
                                "type": "added"
                            },
                            {
                                "content": "            return",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "        cmd_type, cmd_tmpl_g = _guess_exec(procedure_file)",
                                "type": "added"
                            },
                            {
                                "content": "        if not cmd_tmpl:",
                                "type": "context"
                            },
                            {
                                "content": "            cmd_type, cmd_tmpl = _guess_exec(procedure_file)",
                                "type": "deleted"
                            },
                            {
                                "content": "            if cmd_tmpl is None:",
                                "type": "deleted"
                            },
                            {
                                "content": "                raise ValueError(\"No idea how to execute procedure %s. \"",
                                "type": "deleted"
                            },
                            {
                                "content": "                                 \"Missing 'execute' permissions?\",",
                                "type": "deleted"
                            },
                            {
                                "content": "                                 procedure_file)",
                                "type": "deleted"
                            },
                            {
                                "content": "            cmd_tmpl = cmd_tmpl_g",
                                "type": "added"
                            },
                            {
                                "content": "        if not cmd_tmpl:",
                                "type": "added"
                            },
                            {
                                "content": "            raise ValueError(\"No idea how to execute procedure %s. \"",
                                "type": "added"
                            },
                            {
                                "content": "                             \"Missing 'execute' permissions?\",",
                                "type": "added"
                            },
                            {
                                "content": "                             procedure_file)",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "        if help_proc:",
                                "type": "context"
                            },
                            {
                                "content": "            res = get_status_dict(",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "datalad/interface/run_procedure.py",
                            "type": "class",
                            "name": "RunProcedure",
                            "content": "class RunProcedure(Interface):\n    \"\"\"Run prepared procedures (DataLad scripts) on a dataset\n\n    *Concept*\n\n    A \"procedure\" is an algorithm with the purpose to process a dataset in a\n    particular way. Procedures can be useful in a wide range of scenarios,\n    like adjusting dataset configuration in a uniform fashion, populating\n    a dataset with particular content, or automating other routine tasks,\n    such as synchronizing dataset content with certain siblings.\n\n    Implementations of some procedures are shipped together with DataLad,\n    but additional procedures can be provided by 1) any DataLad extension,\n    2) any dataset, 3) a local user, or 4) a local system administrator.\n    DataLad will look for procedures in the following locations and order:\n\n    Directories identified by the configuration settings\n\n    - 'datalad.locations.dataset-procedures'\n    - 'datalad.locations.user-procedures' (determined by\n      appdirs.user_config_dir; defaults to '$HOME/.config/datalad/procedures'\n      on GNU/Linux systems)\n    - 'datalad.locations.system-procedures' (determined by\n      appdirs.site_config_dir; defaults to '/etc/xdg/datalad/procedures' on\n      GNU/Linux systems)\n\n    and subsequently in the 'resources/procedures/' directories of any\n    installed extension, and, lastly, of the DataLad installation itself.\n\n    Each configuration setting can occur multiple times to indicate multiple\n    directories to be searched. If a procedure matching a given name is found\n    (filename without a possible extension), the search is aborted and this\n    implementation will be executed. This makes it possible for individual\n    datasets, users, or machines to override externally provided procedures\n    (enabling the implementation of customizable processing \"hooks\").\n\n\n    *Procedure implementation*\n\n    A procedure can be any executable. Executables must have the appropriate\n    permissions and, in the case of a script, must contain an appropriate\n    \"shebang\" line. If a procedure is not executable, but its filename ends\n    with '.py', it is automatically executed by the 'python' interpreter\n    (whichever version is available in the present environment). Likewise,\n    procedure implementations ending on '.sh' are executed via 'bash'.\n\n    Procedures can implement any argument handling, but must be capable\n    of taking at least one positional argument (the absolute path to the\n    dataset they shall operate on).\n\n\n    *Customize other commands with procedures*\n\n    On execution of any commands, DataLad inspects two additional\n    configuration settings:\n\n    - 'datalad.<name>.proc-pre'\n\n    - 'datalad.<name>.proc-post'\n\n    where '<name>' is the name of a DataLad command. Using this mechanism\n    DataLad can be instructed to run one or more procedures before or\n    after the execution of a given command. For example, configuring\n    a set of metadata types in any newly created dataset can be achieved\n    via:\n\n      % datalad -c 'datalad.create.proc-post=cfg_metadatatypes xmp image' create -d myds\n\n    As procedures run on datasets, it is necessary to explicitly identify\n    the target dataset via the -d (--dataset) option.\n    \"\"\"\n    _params_ = dict(\n        spec=Parameter(\n            args=(\"spec\",),\n            metavar='NAME [ARGS]',\n            nargs=REMAINDER,\n            doc=\"\"\"Name and possibly additional arguments of the\n            to-be-executed procedure.\"\"\"),\n        dataset=Parameter(\n            args=(\"-d\", \"--dataset\"),\n            metavar=\"PATH\",\n            doc=\"\"\"specify the dataset to run the procedure on.\n            An attempt is made to identify the dataset based on the current\n            working directory.\"\"\",\n            constraints=EnsureDataset() | EnsureNone()),\n        discover=Parameter(\n            args=('--discover',),\n            action='store_true',\n            doc=\"\"\"if given, all configured paths are searched for procedures\n            and one result record per discovered procedure is yielded, but\n            no procedure is executed\"\"\"),\n        help_proc=Parameter(\n            args=('--help-proc',),\n            action='store_true',\n            doc=\"\"\"if given, get a help message for procedure NAME from config\n            setting datalad.procedures.NAME.help. [CMD: Note, that this \n            parameter needs to be given before NAME, since it would be regarded \n            as part of ARGS otherwise CMD]\"\"\"\n        )\n    )\n\n    @staticmethod\n    @datasetmethod(name='run_procedure')\n    @eval_results\n    def __call__(\n            spec=None,\n            dataset=None,\n            discover=False,\n            help_proc=False):\n        if not spec and not discover:\n            raise InsufficientArgumentsError('requires at least a procedure name')\n        if help_proc and not spec:\n            raise InsufficientArgumentsError('requires a procedure name')\n\n        try:\n            ds = require_dataset(\n                dataset, check_installed=False,\n                purpose='run a procedure')\n        except NoDatasetArgumentFound:\n            ds = None\n\n        if discover:\n            reported = set()\n            for m, cmd_tmpl, cmd_help in _get_procedure_implementation('*', ds=ds):\n                if m in reported:\n                    continue\n                if not cmd_tmpl:\n                    cmd_type, cmd_tmpl = _guess_exec(m)\n                if cmd_type is None and cmd_tmpl is None:\n                    # doesn't seem like a match\n                    continue\n                res = get_status_dict(\n                    action='run_procedure',\n                    path=m,\n                    type='file',\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='ok',\n                    procedure_type=cmd_type,\n                    procedure_callfmt=cmd_tmpl,\n                    message=cmd_type)\n                reported.add(m)\n                yield res\n            return\n\n        if not isinstance(spec, (tuple, list)):\n            # maybe coming from config\n            import shlex\n            spec = shlex.split(spec)\n        name = spec[0]\n        args = spec[1:]\n\n        try:\n            # get the first match an run with it\n            procedure_file, cmd_tmpl, cmd_help = \\\n                next(_get_procedure_implementation(name, ds=ds))\n        except StopIteration:\n            # TODO error result\n            raise ValueError(\"Cannot find procedure with name '%s'\", name)\n\n        if not cmd_tmpl:\n            cmd_type, cmd_tmpl = _guess_exec(procedure_file)\n            if cmd_tmpl is None:\n                raise ValueError(\"No idea how to execute procedure %s. \"\n                                 \"Missing 'execute' permissions?\",\n                                 procedure_file)\n\n        if help_proc:\n            res = get_status_dict(\n                    action='procedure_help',\n                    path=procedure_file,\n                    type='file',\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='ok',\n                    procedure_callfmt=cmd_tmpl,\n                    message=cmd_help)\n            yield res\n            return\n\n        cmd = cmd_tmpl.format(\n            script=procedure_file,\n            ds=ds.path if ds else '',\n            args=u' '.join(u'\"{}\"'.format(a) for a in args) if args else '')\n        lgr.debug('Attempt to run procedure {} as: {}'.format(\n            name,\n            cmd))\n        for r in Run.__call__(\n                cmd=cmd,\n                dataset=ds,\n                explicit=True,\n                inputs=None,\n                outputs=None,\n                # pass through here\n                on_failure='ignore',\n        ):\n            yield r",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 141,
                            "end_lineno": 337,
                            "parent": null
                        },
                        {
                            "file_name": "datalad/interface/run_procedure.py",
                            "type": "method",
                            "name": "RunProcedure.__call__",
                            "content": "class RunProcedure(Interface):\n    \"\"\"Run prepared procedures (DataLad scripts) on a dataset\n\n    *Concept*\n\n    A \"procedure\" is an algorithm with the purpose to process a dataset in a\n    particular way. Procedures can be useful in a wide range of scenarios,\n    like adjusting dataset configuration in a uniform fashion, populating\n    a dataset with particular content, or automating other routine tasks,\n    such as synchronizing dataset content with certain siblings.\n\n    Implementations of some procedures are shipped together with DataLad,\n    but additional procedures can be provided by 1) any DataLad extension,\n    2) any dataset, 3) a local user, or 4) a local system administrator.\n    DataLad will look for procedures in the following locations and order:\n\n    Directories identified by the configuration settings\n\n    - 'datalad.locations.dataset-procedures'\n    - 'datalad.locations.user-procedures' (determined by\n      appdirs.user_config_dir; defaults to '$HOME/.config/datalad/procedures'\n      on GNU/Linux systems)\n    - 'datalad.locations.system-procedures' (determined by\n      appdirs.site_config_dir; defaults to '/etc/xdg/datalad/procedures' on\n      GNU/Linux systems)\n\n    and subsequently in the 'resources/procedures/' directories of any\n    installed extension, and, lastly, of the DataLad installation itself.\n\n    Each configuration setting can occur multiple times to indicate multiple\n    directories to be searched. If a procedure matching a given name is found\n    (filename without a possible extension), the search is aborted and this\n    implementation will be executed. This makes it possible for individual\n    datasets, users, or machines to override externally provided procedures\n    (enabling the implementation of customizable processing \"hooks\").\n\n\n    *Procedure implementation*\n\n    A procedure can be any executable. Executables must have the appropriate\n    permissions and, in the case of a script, must contain an appropriate\n    \"shebang\" line. If a procedure is not executable, but its filename ends\n    with '.py', it is automatically executed by the 'python' interpreter\n    (whichever version is available in the present environment). Likewise,\n    procedure implementations ending on '.sh' are executed via 'bash'.\n\n    Procedures can implement any argument handling, but must be capable\n    of taking at least one positional argument (the absolute path to the\n    dataset they shall operate on).\n\n\n    *Customize other commands with procedures*\n\n    On execution of any commands, DataLad inspects two additional\n    configuration settings:\n\n    - 'datalad.<name>.proc-pre'\n\n    - 'datalad.<name>.proc-post'\n\n    where '<name>' is the name of a DataLad command. Using this mechanism\n    DataLad can be instructed to run one or more procedures before or\n    after the execution of a given command. For example, configuring\n    a set of metadata types in any newly created dataset can be achieved\n    via:\n\n      % datalad -c 'datalad.create.proc-post=cfg_metadatatypes xmp image' create -d myds\n\n    As procedures run on datasets, it is necessary to explicitly identify\n    the target dataset via the -d (--dataset) option.\n    \"\"\"\n    _params_ = dict(\n        spec=Parameter(\n            args=(\"spec\",),\n            metavar='NAME [ARGS]',\n            nargs=REMAINDER,\n            doc=\"\"\"Name and possibly additional arguments of the\n            to-be-executed procedure.\"\"\"),\n        dataset=Parameter(\n            args=(\"-d\", \"--dataset\"),\n            metavar=\"PATH\",\n            doc=\"\"\"specify the dataset to run the procedure on.\n            An attempt is made to identify the dataset based on the current\n            working directory.\"\"\",\n            constraints=EnsureDataset() | EnsureNone()),\n        discover=Parameter(\n            args=('--discover',),\n            action='store_true',\n            doc=\"\"\"if given, all configured paths are searched for procedures\n            and one result record per discovered procedure is yielded, but\n            no procedure is executed\"\"\"),\n        help_proc=Parameter(\n            args=('--help-proc',),\n            action='store_true',\n            doc=\"\"\"if given, get a help message for procedure NAME from config\n            setting datalad.procedures.NAME.help. [CMD: Note, that this \n            parameter needs to be given before NAME, since it would be regarded \n            as part of ARGS otherwise CMD]\"\"\"\n        )\n    )\n\n    @staticmethod\n    @datasetmethod(name='run_procedure')\n    @eval_results\n    def __call__(\n            spec=None,\n            dataset=None,\n            discover=False,\n            help_proc=False):\n        if not spec and not discover:\n            raise InsufficientArgumentsError('requires at least a procedure name')\n        if help_proc and not spec:\n            raise InsufficientArgumentsError('requires a procedure name')\n\n        try:\n            ds = require_dataset(\n                dataset, check_installed=False,\n                purpose='run a procedure')\n        except NoDatasetArgumentFound:\n            ds = None\n\n        if discover:\n            reported = set()\n            for m, cmd_tmpl, cmd_help in _get_procedure_implementation('*', ds=ds):\n                if m in reported:\n                    continue\n                if not cmd_tmpl:\n                    cmd_type, cmd_tmpl = _guess_exec(m)\n                if cmd_type is None and cmd_tmpl is None:\n                    # doesn't seem like a match\n                    continue\n                res = get_status_dict(\n                    action='run_procedure',\n                    path=m,\n                    type='file',\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='ok',\n                    procedure_type=cmd_type,\n                    procedure_callfmt=cmd_tmpl,\n                    message=cmd_type)\n                reported.add(m)\n                yield res\n            return\n\n        if not isinstance(spec, (tuple, list)):\n            # maybe coming from config\n            import shlex\n            spec = shlex.split(spec)\n        name = spec[0]\n        args = spec[1:]\n\n        try:\n            # get the first match an run with it\n            procedure_file, cmd_tmpl, cmd_help = \\\n                next(_get_procedure_implementation(name, ds=ds))\n        except StopIteration:\n            # TODO error result\n            raise ValueError(\"Cannot find procedure with name '%s'\", name)\n\n        if not cmd_tmpl:\n            cmd_type, cmd_tmpl = _guess_exec(procedure_file)\n            if cmd_tmpl is None:\n                raise ValueError(\"No idea how to execute procedure %s. \"\n                                 \"Missing 'execute' permissions?\",\n                                 procedure_file)\n\n        if help_proc:\n            res = get_status_dict(\n                    action='procedure_help',\n                    path=procedure_file,\n                    type='file',\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='ok',\n                    procedure_callfmt=cmd_tmpl,\n                    message=cmd_help)\n            yield res\n            return\n\n        cmd = cmd_tmpl.format(\n            script=procedure_file,\n            ds=ds.path if ds else '',\n            args=u' '.join(u'\"{}\"'.format(a) for a in args) if args else '')\n        lgr.debug('Attempt to run procedure {} as: {}'.format(\n            name,\n            cmd))\n        for r in Run.__call__(\n                cmd=cmd,\n                dataset=ds,\n                explicit=True,\n                inputs=None,\n                outputs=None,\n                # pass through here\n                on_failure='ignore',\n        ):\n            yield r",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 245,
                            "end_lineno": 337,
                            "parent": {
                                "file_name": "datalad/interface/run_procedure.py",
                                "type": "class",
                                "name": "RunProcedure",
                                "content": "class RunProcedure(Interface):\n    \"\"\"Run prepared procedures (DataLad scripts) on a dataset\n\n    *Concept*\n\n    A \"procedure\" is an algorithm with the purpose to process a dataset in a\n    particular way. Procedures can be useful in a wide range of scenarios,\n    like adjusting dataset configuration in a uniform fashion, populating\n    a dataset with particular content, or automating other routine tasks,\n    such as synchronizing dataset content with certain siblings.\n\n    Implementations of some procedures are shipped together with DataLad,\n    but additional procedures can be provided by 1) any DataLad extension,\n    2) any dataset, 3) a local user, or 4) a local system administrator.\n    DataLad will look for procedures in the following locations and order:\n\n    Directories identified by the configuration settings\n\n    - 'datalad.locations.dataset-procedures'\n    - 'datalad.locations.user-procedures' (determined by\n      appdirs.user_config_dir; defaults to '$HOME/.config/datalad/procedures'\n      on GNU/Linux systems)\n    - 'datalad.locations.system-procedures' (determined by\n      appdirs.site_config_dir; defaults to '/etc/xdg/datalad/procedures' on\n      GNU/Linux systems)\n\n    and subsequently in the 'resources/procedures/' directories of any\n    installed extension, and, lastly, of the DataLad installation itself.\n\n    Each configuration setting can occur multiple times to indicate multiple\n    directories to be searched. If a procedure matching a given name is found\n    (filename without a possible extension), the search is aborted and this\n    implementation will be executed. This makes it possible for individual\n    datasets, users, or machines to override externally provided procedures\n    (enabling the implementation of customizable processing \"hooks\").\n\n\n    *Procedure implementation*\n\n    A procedure can be any executable. Executables must have the appropriate\n    permissions and, in the case of a script, must contain an appropriate\n    \"shebang\" line. If a procedure is not executable, but its filename ends\n    with '.py', it is automatically executed by the 'python' interpreter\n    (whichever version is available in the present environment). Likewise,\n    procedure implementations ending on '.sh' are executed via 'bash'.\n\n    Procedures can implement any argument handling, but must be capable\n    of taking at least one positional argument (the absolute path to the\n    dataset they shall operate on).\n\n\n    *Customize other commands with procedures*\n\n    On execution of any commands, DataLad inspects two additional\n    configuration settings:\n\n    - 'datalad.<name>.proc-pre'\n\n    - 'datalad.<name>.proc-post'\n\n    where '<name>' is the name of a DataLad command. Using this mechanism\n    DataLad can be instructed to run one or more procedures before or\n    after the execution of a given command. For example, configuring\n    a set of metadata types in any newly created dataset can be achieved\n    via:\n\n      % datalad -c 'datalad.create.proc-post=cfg_metadatatypes xmp image' create -d myds\n\n    As procedures run on datasets, it is necessary to explicitly identify\n    the target dataset via the -d (--dataset) option.\n    \"\"\"\n    _params_ = dict(\n        spec=Parameter(\n            args=(\"spec\",),\n            metavar='NAME [ARGS]',\n            nargs=REMAINDER,\n            doc=\"\"\"Name and possibly additional arguments of the\n            to-be-executed procedure.\"\"\"),\n        dataset=Parameter(\n            args=(\"-d\", \"--dataset\"),\n            metavar=\"PATH\",\n            doc=\"\"\"specify the dataset to run the procedure on.\n            An attempt is made to identify the dataset based on the current\n            working directory.\"\"\",\n            constraints=EnsureDataset() | EnsureNone()),\n        discover=Parameter(\n            args=('--discover',),\n            action='store_true',\n            doc=\"\"\"if given, all configured paths are searched for procedures\n            and one result record per discovered procedure is yielded, but\n            no procedure is executed\"\"\"),\n        help_proc=Parameter(\n            args=('--help-proc',),\n            action='store_true',\n            doc=\"\"\"if given, get a help message for procedure NAME from config\n            setting datalad.procedures.NAME.help. [CMD: Note, that this \n            parameter needs to be given before NAME, since it would be regarded \n            as part of ARGS otherwise CMD]\"\"\"\n        )\n    )\n\n    @staticmethod\n    @datasetmethod(name='run_procedure')\n    @eval_results\n    def __call__(\n            spec=None,\n            dataset=None,\n            discover=False,\n            help_proc=False):\n        if not spec and not discover:\n            raise InsufficientArgumentsError('requires at least a procedure name')\n        if help_proc and not spec:\n            raise InsufficientArgumentsError('requires a procedure name')\n\n        try:\n            ds = require_dataset(\n                dataset, check_installed=False,\n                purpose='run a procedure')\n        except NoDatasetArgumentFound:\n            ds = None\n\n        if discover:\n            reported = set()\n            for m, cmd_tmpl, cmd_help in _get_procedure_implementation('*', ds=ds):\n                if m in reported:\n                    continue\n                if not cmd_tmpl:\n                    cmd_type, cmd_tmpl = _guess_exec(m)\n                if cmd_type is None and cmd_tmpl is None:\n                    # doesn't seem like a match\n                    continue\n                res = get_status_dict(\n                    action='run_procedure',\n                    path=m,\n                    type='file',\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='ok',\n                    procedure_type=cmd_type,\n                    procedure_callfmt=cmd_tmpl,\n                    message=cmd_type)\n                reported.add(m)\n                yield res\n            return\n\n        if not isinstance(spec, (tuple, list)):\n            # maybe coming from config\n            import shlex\n            spec = shlex.split(spec)\n        name = spec[0]\n        args = spec[1:]\n\n        try:\n            # get the first match an run with it\n            procedure_file, cmd_tmpl, cmd_help = \\\n                next(_get_procedure_implementation(name, ds=ds))\n        except StopIteration:\n            # TODO error result\n            raise ValueError(\"Cannot find procedure with name '%s'\", name)\n\n        if not cmd_tmpl:\n            cmd_type, cmd_tmpl = _guess_exec(procedure_file)\n            if cmd_tmpl is None:\n                raise ValueError(\"No idea how to execute procedure %s. \"\n                                 \"Missing 'execute' permissions?\",\n                                 procedure_file)\n\n        if help_proc:\n            res = get_status_dict(\n                    action='procedure_help',\n                    path=procedure_file,\n                    type='file',\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='ok',\n                    procedure_callfmt=cmd_tmpl,\n                    message=cmd_help)\n            yield res\n            return\n\n        cmd = cmd_tmpl.format(\n            script=procedure_file,\n            ds=ds.path if ds else '',\n            args=u' '.join(u'\"{}\"'.format(a) for a in args) if args else '')\n        lgr.debug('Attempt to run procedure {} as: {}'.format(\n            name,\n            cmd))\n        for r in Run.__call__(\n                cmd=cmd,\n                dataset=ds,\n                explicit=True,\n                inputs=None,\n                outputs=None,\n                # pass through here\n                on_failure='ignore',\n        ):\n            yield r",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 141,
                                "end_lineno": 337,
                                "parent": null
                            }
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 313,
                            "length": 6
                        },
                        "new_range": {
                            "start": 324,
                            "length": 7
                        },
                        "section": "class RunProcedure(Interface):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "                    logger=lgr,",
                                "type": "context"
                            },
                            {
                                "content": "                    refds=ds.path if ds else None,",
                                "type": "context"
                            },
                            {
                                "content": "                    status='ok',",
                                "type": "context"
                            },
                            {
                                "content": "                    procedure_type=cmd_type,",
                                "type": "added"
                            },
                            {
                                "content": "                    procedure_callfmt=cmd_tmpl,",
                                "type": "context"
                            },
                            {
                                "content": "                    message=cmd_help)",
                                "type": "context"
                            },
                            {
                                "content": "            yield res",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "datalad/interface/run_procedure.py",
                            "type": "class",
                            "name": "RunProcedure",
                            "content": "class RunProcedure(Interface):\n    \"\"\"Run prepared procedures (DataLad scripts) on a dataset\n\n    *Concept*\n\n    A \"procedure\" is an algorithm with the purpose to process a dataset in a\n    particular way. Procedures can be useful in a wide range of scenarios,\n    like adjusting dataset configuration in a uniform fashion, populating\n    a dataset with particular content, or automating other routine tasks,\n    such as synchronizing dataset content with certain siblings.\n\n    Implementations of some procedures are shipped together with DataLad,\n    but additional procedures can be provided by 1) any DataLad extension,\n    2) any dataset, 3) a local user, or 4) a local system administrator.\n    DataLad will look for procedures in the following locations and order:\n\n    Directories identified by the configuration settings\n\n    - 'datalad.locations.dataset-procedures'\n    - 'datalad.locations.user-procedures' (determined by\n      appdirs.user_config_dir; defaults to '$HOME/.config/datalad/procedures'\n      on GNU/Linux systems)\n    - 'datalad.locations.system-procedures' (determined by\n      appdirs.site_config_dir; defaults to '/etc/xdg/datalad/procedures' on\n      GNU/Linux systems)\n\n    and subsequently in the 'resources/procedures/' directories of any\n    installed extension, and, lastly, of the DataLad installation itself.\n\n    Each configuration setting can occur multiple times to indicate multiple\n    directories to be searched. If a procedure matching a given name is found\n    (filename without a possible extension), the search is aborted and this\n    implementation will be executed. This makes it possible for individual\n    datasets, users, or machines to override externally provided procedures\n    (enabling the implementation of customizable processing \"hooks\").\n\n\n    *Procedure implementation*\n\n    A procedure can be any executable. Executables must have the appropriate\n    permissions and, in the case of a script, must contain an appropriate\n    \"shebang\" line. If a procedure is not executable, but its filename ends\n    with '.py', it is automatically executed by the 'python' interpreter\n    (whichever version is available in the present environment). Likewise,\n    procedure implementations ending on '.sh' are executed via 'bash'.\n\n    Procedures can implement any argument handling, but must be capable\n    of taking at least one positional argument (the absolute path to the\n    dataset they shall operate on).\n\n\n    *Customize other commands with procedures*\n\n    On execution of any commands, DataLad inspects two additional\n    configuration settings:\n\n    - 'datalad.<name>.proc-pre'\n\n    - 'datalad.<name>.proc-post'\n\n    where '<name>' is the name of a DataLad command. Using this mechanism\n    DataLad can be instructed to run one or more procedures before or\n    after the execution of a given command. For example, configuring\n    a set of metadata types in any newly created dataset can be achieved\n    via:\n\n      % datalad -c 'datalad.create.proc-post=cfg_metadatatypes xmp image' create -d myds\n\n    As procedures run on datasets, it is necessary to explicitly identify\n    the target dataset via the -d (--dataset) option.\n    \"\"\"\n    _params_ = dict(\n        spec=Parameter(\n            args=(\"spec\",),\n            metavar='NAME [ARGS]',\n            nargs=REMAINDER,\n            doc=\"\"\"Name and possibly additional arguments of the\n            to-be-executed procedure.\"\"\"),\n        dataset=Parameter(\n            args=(\"-d\", \"--dataset\"),\n            metavar=\"PATH\",\n            doc=\"\"\"specify the dataset to run the procedure on.\n            An attempt is made to identify the dataset based on the current\n            working directory.\"\"\",\n            constraints=EnsureDataset() | EnsureNone()),\n        discover=Parameter(\n            args=('--discover',),\n            action='store_true',\n            doc=\"\"\"if given, all configured paths are searched for procedures\n            and one result record per discovered procedure is yielded, but\n            no procedure is executed\"\"\"),\n        help_proc=Parameter(\n            args=('--help-proc',),\n            action='store_true',\n            doc=\"\"\"if given, get a help message for procedure NAME from config\n            setting datalad.procedures.NAME.help. [CMD: Note, that this \n            parameter needs to be given before NAME, since it would be regarded \n            as part of ARGS otherwise CMD]\"\"\"\n        )\n    )\n\n    @staticmethod\n    @datasetmethod(name='run_procedure')\n    @eval_results\n    def __call__(\n            spec=None,\n            dataset=None,\n            discover=False,\n            help_proc=False):\n        if not spec and not discover:\n            raise InsufficientArgumentsError('requires at least a procedure name')\n        if help_proc and not spec:\n            raise InsufficientArgumentsError('requires a procedure name')\n\n        try:\n            ds = require_dataset(\n                dataset, check_installed=False,\n                purpose='run a procedure')\n        except NoDatasetArgumentFound:\n            ds = None\n\n        if discover:\n            reported = set()\n            for m, cmd_tmpl, cmd_help in _get_procedure_implementation('*', ds=ds):\n                if m in reported:\n                    continue\n                cmd_type, cmd_tmpl_g = _guess_exec(m)\n                if not cmd_tmpl:\n                    cmd_tmpl = cmd_tmpl_g\n                if cmd_type is None and cmd_tmpl is None:\n                    # doesn't seem like a match\n                    continue\n                res = get_status_dict(\n                    action='run_procedure',\n                    path=m,\n                    type='file',\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='ok',\n                    procedure_type=cmd_type,\n                    procedure_callfmt=cmd_tmpl,\n                    message=cmd_type)\n                reported.add(m)\n                yield res\n            return\n\n        if not isinstance(spec, (tuple, list)):\n            # maybe coming from config\n            import shlex\n            spec = shlex.split(spec)\n        name = spec[0]\n        args = spec[1:]\n\n        try:\n            # get the first match an run with it\n            procedure_file, cmd_tmpl, cmd_help = \\\n                next(_get_procedure_implementation(name, ds=ds))\n        except StopIteration:\n            res = get_status_dict(\n                    action='run_procedure',\n                    # TODO: Default renderer requires a key \"path\" to exist.\n                    # Doesn't make a lot of sense in this case\n                    path=name,\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='impossible',\n                    message=\"Cannot find procedure with name '%s'\" % name)\n            yield res\n            return\n\n        cmd_type, cmd_tmpl_g = _guess_exec(procedure_file)\n        if not cmd_tmpl:\n            cmd_tmpl = cmd_tmpl_g\n        if not cmd_tmpl:\n            raise ValueError(\"No idea how to execute procedure %s. \"\n                             \"Missing 'execute' permissions?\",\n                             procedure_file)\n\n        if help_proc:\n            res = get_status_dict(\n                    action='procedure_help',\n                    path=procedure_file,\n                    type='file',\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='ok',\n                    procedure_type=cmd_type,\n                    procedure_callfmt=cmd_tmpl,\n                    message=cmd_help)\n            yield res\n            return\n\n        cmd = cmd_tmpl.format(\n            script=procedure_file,\n            ds=ds.path if ds else '',\n            args=u' '.join(u'\"{}\"'.format(a) for a in args) if args else '')\n        lgr.debug('Attempt to run procedure {} as: {}'.format(\n            name,\n            cmd))\n        for r in Run.__call__(\n                cmd=cmd,\n                dataset=ds,\n                explicit=True,\n                inputs=None,\n                outputs=None,\n                # pass through here\n                on_failure='ignore',\n        ):\n            yield r",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 141,
                            "end_lineno": 349,
                            "parent": null
                        },
                        {
                            "file_name": "datalad/interface/run_procedure.py",
                            "type": "method",
                            "name": "RunProcedure.__call__",
                            "content": "class RunProcedure(Interface):\n    \"\"\"Run prepared procedures (DataLad scripts) on a dataset\n\n    *Concept*\n\n    A \"procedure\" is an algorithm with the purpose to process a dataset in a\n    particular way. Procedures can be useful in a wide range of scenarios,\n    like adjusting dataset configuration in a uniform fashion, populating\n    a dataset with particular content, or automating other routine tasks,\n    such as synchronizing dataset content with certain siblings.\n\n    Implementations of some procedures are shipped together with DataLad,\n    but additional procedures can be provided by 1) any DataLad extension,\n    2) any dataset, 3) a local user, or 4) a local system administrator.\n    DataLad will look for procedures in the following locations and order:\n\n    Directories identified by the configuration settings\n\n    - 'datalad.locations.dataset-procedures'\n    - 'datalad.locations.user-procedures' (determined by\n      appdirs.user_config_dir; defaults to '$HOME/.config/datalad/procedures'\n      on GNU/Linux systems)\n    - 'datalad.locations.system-procedures' (determined by\n      appdirs.site_config_dir; defaults to '/etc/xdg/datalad/procedures' on\n      GNU/Linux systems)\n\n    and subsequently in the 'resources/procedures/' directories of any\n    installed extension, and, lastly, of the DataLad installation itself.\n\n    Each configuration setting can occur multiple times to indicate multiple\n    directories to be searched. If a procedure matching a given name is found\n    (filename without a possible extension), the search is aborted and this\n    implementation will be executed. This makes it possible for individual\n    datasets, users, or machines to override externally provided procedures\n    (enabling the implementation of customizable processing \"hooks\").\n\n\n    *Procedure implementation*\n\n    A procedure can be any executable. Executables must have the appropriate\n    permissions and, in the case of a script, must contain an appropriate\n    \"shebang\" line. If a procedure is not executable, but its filename ends\n    with '.py', it is automatically executed by the 'python' interpreter\n    (whichever version is available in the present environment). Likewise,\n    procedure implementations ending on '.sh' are executed via 'bash'.\n\n    Procedures can implement any argument handling, but must be capable\n    of taking at least one positional argument (the absolute path to the\n    dataset they shall operate on).\n\n\n    *Customize other commands with procedures*\n\n    On execution of any commands, DataLad inspects two additional\n    configuration settings:\n\n    - 'datalad.<name>.proc-pre'\n\n    - 'datalad.<name>.proc-post'\n\n    where '<name>' is the name of a DataLad command. Using this mechanism\n    DataLad can be instructed to run one or more procedures before or\n    after the execution of a given command. For example, configuring\n    a set of metadata types in any newly created dataset can be achieved\n    via:\n\n      % datalad -c 'datalad.create.proc-post=cfg_metadatatypes xmp image' create -d myds\n\n    As procedures run on datasets, it is necessary to explicitly identify\n    the target dataset via the -d (--dataset) option.\n    \"\"\"\n    _params_ = dict(\n        spec=Parameter(\n            args=(\"spec\",),\n            metavar='NAME [ARGS]',\n            nargs=REMAINDER,\n            doc=\"\"\"Name and possibly additional arguments of the\n            to-be-executed procedure.\"\"\"),\n        dataset=Parameter(\n            args=(\"-d\", \"--dataset\"),\n            metavar=\"PATH\",\n            doc=\"\"\"specify the dataset to run the procedure on.\n            An attempt is made to identify the dataset based on the current\n            working directory.\"\"\",\n            constraints=EnsureDataset() | EnsureNone()),\n        discover=Parameter(\n            args=('--discover',),\n            action='store_true',\n            doc=\"\"\"if given, all configured paths are searched for procedures\n            and one result record per discovered procedure is yielded, but\n            no procedure is executed\"\"\"),\n        help_proc=Parameter(\n            args=('--help-proc',),\n            action='store_true',\n            doc=\"\"\"if given, get a help message for procedure NAME from config\n            setting datalad.procedures.NAME.help. [CMD: Note, that this \n            parameter needs to be given before NAME, since it would be regarded \n            as part of ARGS otherwise CMD]\"\"\"\n        )\n    )\n\n    @staticmethod\n    @datasetmethod(name='run_procedure')\n    @eval_results\n    def __call__(\n            spec=None,\n            dataset=None,\n            discover=False,\n            help_proc=False):\n        if not spec and not discover:\n            raise InsufficientArgumentsError('requires at least a procedure name')\n        if help_proc and not spec:\n            raise InsufficientArgumentsError('requires a procedure name')\n\n        try:\n            ds = require_dataset(\n                dataset, check_installed=False,\n                purpose='run a procedure')\n        except NoDatasetArgumentFound:\n            ds = None\n\n        if discover:\n            reported = set()\n            for m, cmd_tmpl, cmd_help in _get_procedure_implementation('*', ds=ds):\n                if m in reported:\n                    continue\n                cmd_type, cmd_tmpl_g = _guess_exec(m)\n                if not cmd_tmpl:\n                    cmd_tmpl = cmd_tmpl_g\n                if cmd_type is None and cmd_tmpl is None:\n                    # doesn't seem like a match\n                    continue\n                res = get_status_dict(\n                    action='run_procedure',\n                    path=m,\n                    type='file',\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='ok',\n                    procedure_type=cmd_type,\n                    procedure_callfmt=cmd_tmpl,\n                    message=cmd_type)\n                reported.add(m)\n                yield res\n            return\n\n        if not isinstance(spec, (tuple, list)):\n            # maybe coming from config\n            import shlex\n            spec = shlex.split(spec)\n        name = spec[0]\n        args = spec[1:]\n\n        try:\n            # get the first match an run with it\n            procedure_file, cmd_tmpl, cmd_help = \\\n                next(_get_procedure_implementation(name, ds=ds))\n        except StopIteration:\n            res = get_status_dict(\n                    action='run_procedure',\n                    # TODO: Default renderer requires a key \"path\" to exist.\n                    # Doesn't make a lot of sense in this case\n                    path=name,\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='impossible',\n                    message=\"Cannot find procedure with name '%s'\" % name)\n            yield res\n            return\n\n        cmd_type, cmd_tmpl_g = _guess_exec(procedure_file)\n        if not cmd_tmpl:\n            cmd_tmpl = cmd_tmpl_g\n        if not cmd_tmpl:\n            raise ValueError(\"No idea how to execute procedure %s. \"\n                             \"Missing 'execute' permissions?\",\n                             procedure_file)\n\n        if help_proc:\n            res = get_status_dict(\n                    action='procedure_help',\n                    path=procedure_file,\n                    type='file',\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='ok',\n                    procedure_type=cmd_type,\n                    procedure_callfmt=cmd_tmpl,\n                    message=cmd_help)\n            yield res\n            return\n\n        cmd = cmd_tmpl.format(\n            script=procedure_file,\n            ds=ds.path if ds else '',\n            args=u' '.join(u'\"{}\"'.format(a) for a in args) if args else '')\n        lgr.debug('Attempt to run procedure {} as: {}'.format(\n            name,\n            cmd))\n        for r in Run.__call__(\n                cmd=cmd,\n                dataset=ds,\n                explicit=True,\n                inputs=None,\n                outputs=None,\n                # pass through here\n                on_failure='ignore',\n        ):\n            yield r",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 245,
                            "end_lineno": 349,
                            "parent": {
                                "file_name": "datalad/interface/run_procedure.py",
                                "type": "class",
                                "name": "RunProcedure",
                                "content": "class RunProcedure(Interface):\n    \"\"\"Run prepared procedures (DataLad scripts) on a dataset\n\n    *Concept*\n\n    A \"procedure\" is an algorithm with the purpose to process a dataset in a\n    particular way. Procedures can be useful in a wide range of scenarios,\n    like adjusting dataset configuration in a uniform fashion, populating\n    a dataset with particular content, or automating other routine tasks,\n    such as synchronizing dataset content with certain siblings.\n\n    Implementations of some procedures are shipped together with DataLad,\n    but additional procedures can be provided by 1) any DataLad extension,\n    2) any dataset, 3) a local user, or 4) a local system administrator.\n    DataLad will look for procedures in the following locations and order:\n\n    Directories identified by the configuration settings\n\n    - 'datalad.locations.dataset-procedures'\n    - 'datalad.locations.user-procedures' (determined by\n      appdirs.user_config_dir; defaults to '$HOME/.config/datalad/procedures'\n      on GNU/Linux systems)\n    - 'datalad.locations.system-procedures' (determined by\n      appdirs.site_config_dir; defaults to '/etc/xdg/datalad/procedures' on\n      GNU/Linux systems)\n\n    and subsequently in the 'resources/procedures/' directories of any\n    installed extension, and, lastly, of the DataLad installation itself.\n\n    Each configuration setting can occur multiple times to indicate multiple\n    directories to be searched. If a procedure matching a given name is found\n    (filename without a possible extension), the search is aborted and this\n    implementation will be executed. This makes it possible for individual\n    datasets, users, or machines to override externally provided procedures\n    (enabling the implementation of customizable processing \"hooks\").\n\n\n    *Procedure implementation*\n\n    A procedure can be any executable. Executables must have the appropriate\n    permissions and, in the case of a script, must contain an appropriate\n    \"shebang\" line. If a procedure is not executable, but its filename ends\n    with '.py', it is automatically executed by the 'python' interpreter\n    (whichever version is available in the present environment). Likewise,\n    procedure implementations ending on '.sh' are executed via 'bash'.\n\n    Procedures can implement any argument handling, but must be capable\n    of taking at least one positional argument (the absolute path to the\n    dataset they shall operate on).\n\n\n    *Customize other commands with procedures*\n\n    On execution of any commands, DataLad inspects two additional\n    configuration settings:\n\n    - 'datalad.<name>.proc-pre'\n\n    - 'datalad.<name>.proc-post'\n\n    where '<name>' is the name of a DataLad command. Using this mechanism\n    DataLad can be instructed to run one or more procedures before or\n    after the execution of a given command. For example, configuring\n    a set of metadata types in any newly created dataset can be achieved\n    via:\n\n      % datalad -c 'datalad.create.proc-post=cfg_metadatatypes xmp image' create -d myds\n\n    As procedures run on datasets, it is necessary to explicitly identify\n    the target dataset via the -d (--dataset) option.\n    \"\"\"\n    _params_ = dict(\n        spec=Parameter(\n            args=(\"spec\",),\n            metavar='NAME [ARGS]',\n            nargs=REMAINDER,\n            doc=\"\"\"Name and possibly additional arguments of the\n            to-be-executed procedure.\"\"\"),\n        dataset=Parameter(\n            args=(\"-d\", \"--dataset\"),\n            metavar=\"PATH\",\n            doc=\"\"\"specify the dataset to run the procedure on.\n            An attempt is made to identify the dataset based on the current\n            working directory.\"\"\",\n            constraints=EnsureDataset() | EnsureNone()),\n        discover=Parameter(\n            args=('--discover',),\n            action='store_true',\n            doc=\"\"\"if given, all configured paths are searched for procedures\n            and one result record per discovered procedure is yielded, but\n            no procedure is executed\"\"\"),\n        help_proc=Parameter(\n            args=('--help-proc',),\n            action='store_true',\n            doc=\"\"\"if given, get a help message for procedure NAME from config\n            setting datalad.procedures.NAME.help. [CMD: Note, that this \n            parameter needs to be given before NAME, since it would be regarded \n            as part of ARGS otherwise CMD]\"\"\"\n        )\n    )\n\n    @staticmethod\n    @datasetmethod(name='run_procedure')\n    @eval_results\n    def __call__(\n            spec=None,\n            dataset=None,\n            discover=False,\n            help_proc=False):\n        if not spec and not discover:\n            raise InsufficientArgumentsError('requires at least a procedure name')\n        if help_proc and not spec:\n            raise InsufficientArgumentsError('requires a procedure name')\n\n        try:\n            ds = require_dataset(\n                dataset, check_installed=False,\n                purpose='run a procedure')\n        except NoDatasetArgumentFound:\n            ds = None\n\n        if discover:\n            reported = set()\n            for m, cmd_tmpl, cmd_help in _get_procedure_implementation('*', ds=ds):\n                if m in reported:\n                    continue\n                cmd_type, cmd_tmpl_g = _guess_exec(m)\n                if not cmd_tmpl:\n                    cmd_tmpl = cmd_tmpl_g\n                if cmd_type is None and cmd_tmpl is None:\n                    # doesn't seem like a match\n                    continue\n                res = get_status_dict(\n                    action='run_procedure',\n                    path=m,\n                    type='file',\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='ok',\n                    procedure_type=cmd_type,\n                    procedure_callfmt=cmd_tmpl,\n                    message=cmd_type)\n                reported.add(m)\n                yield res\n            return\n\n        if not isinstance(spec, (tuple, list)):\n            # maybe coming from config\n            import shlex\n            spec = shlex.split(spec)\n        name = spec[0]\n        args = spec[1:]\n\n        try:\n            # get the first match an run with it\n            procedure_file, cmd_tmpl, cmd_help = \\\n                next(_get_procedure_implementation(name, ds=ds))\n        except StopIteration:\n            res = get_status_dict(\n                    action='run_procedure',\n                    # TODO: Default renderer requires a key \"path\" to exist.\n                    # Doesn't make a lot of sense in this case\n                    path=name,\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='impossible',\n                    message=\"Cannot find procedure with name '%s'\" % name)\n            yield res\n            return\n\n        cmd_type, cmd_tmpl_g = _guess_exec(procedure_file)\n        if not cmd_tmpl:\n            cmd_tmpl = cmd_tmpl_g\n        if not cmd_tmpl:\n            raise ValueError(\"No idea how to execute procedure %s. \"\n                             \"Missing 'execute' permissions?\",\n                             procedure_file)\n\n        if help_proc:\n            res = get_status_dict(\n                    action='procedure_help',\n                    path=procedure_file,\n                    type='file',\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='ok',\n                    procedure_type=cmd_type,\n                    procedure_callfmt=cmd_tmpl,\n                    message=cmd_help)\n            yield res\n            return\n\n        cmd = cmd_tmpl.format(\n            script=procedure_file,\n            ds=ds.path if ds else '',\n            args=u' '.join(u'\"{}\"'.format(a) for a in args) if args else '')\n        lgr.debug('Attempt to run procedure {} as: {}'.format(\n            name,\n            cmd))\n        for r in Run.__call__(\n                cmd=cmd,\n                dataset=ds,\n                explicit=True,\n                inputs=None,\n                outputs=None,\n                # pass through here\n                on_failure='ignore',\n        ):\n            yield r",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 141,
                                "end_lineno": 349,
                                "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\"\"\"test command datalad run-procedure\n\n\"\"\"\n\n__docformat__ = 'restructuredtext'\n\nimport os.path as op\n\nfrom datalad.tests.utils import ok_clean_git\nfrom datalad.tests.utils import eq_\nfrom datalad.tests.utils import ok_file_has_content\nfrom datalad.tests.utils import with_tree\nfrom datalad.tests.utils import with_tempfile\nfrom datalad.tests.utils import assert_raises\nfrom datalad.tests.utils import assert_true\nfrom datalad.tests.utils import assert_in_results\nfrom datalad.tests.utils import skip_if\nfrom datalad.tests.utils import on_windows\nfrom datalad.distribution.dataset import Dataset\nfrom datalad.support.exceptions import InsufficientArgumentsError\nfrom datalad.api import run_procedure\nfrom datalad.api import clean\nfrom datalad import cfg\n\n\ndef test_invalid_call():\n    # needs spec or discover\n    assert_raises(InsufficientArgumentsError, run_procedure)\n\n\n# FIXME: For some reason fails to commit correctly if on windows and in direct\n# mode. However, direct mode on linux works\n@skip_if(cond=on_windows and cfg.get(\"datalad.repo.version\", None) != 6)\n@with_tree(tree={\n    'code': {'datalad_test_proc.py': \"\"\"\\\nimport sys\nimport os.path as op\nfrom datalad.api import add, Dataset\n\nwith open(op.join(sys.argv[1], 'fromproc.txt'), 'w') as f:\n    f.write('hello\\\\n')\nadd(dataset=Dataset(sys.argv[1]), path='fromproc.txt')\n\"\"\"}})\n@with_tempfile\ndef test_basics(path, super_path):\n    ds = Dataset(path).create(force=True)\n    # TODO: this procedure would leave a clean dataset, but `run` cannot handle dirty\n    # input yet, so manual for now\n    # V6FACT: this leaves the file staged, but not committed\n    ds.add('code', to_git=True)\n    # V6FACT: even this leaves it staged\n    ds.add('.')\n    # V6FACT: but this finally commits it\n    ds.save()\n    # TODO remove above two lines\n    ds.run_procedure('setup_yoda_dataset')\n    ok_clean_git(ds.path)\n    # configure dataset to look for procedures in its code folder\n    ds.config.add(\n        'datalad.locations.dataset-procedures',\n        'code',\n        where='dataset')\n    # commit this procedure config for later use in a clone:\n    ds.add(op.join('.datalad', 'config'))\n    # configure dataset to run the demo procedure prior to the clean command\n    ds.config.add(\n        'datalad.clean.proc-pre',\n        'datalad_test_proc',\n        where='dataset')\n    # run command that should trigger the demo procedure\n    ds.clean()\n    # look for traces\n    ok_file_has_content(op.join(ds.path, 'fromproc.txt'), 'hello\\n')\n    ok_clean_git(ds.path, index_modified=[op.join('.datalad', 'config')])\n\n    # make a fresh dataset:\n    super = Dataset(super_path).create()\n    # configure dataset to run the demo procedure prior to the clean command\n    super.config.add(\n        'datalad.clean.proc-pre',\n        'datalad_test_proc',\n        where='dataset')\n    # 'super' doesn't know any procedures but should get to know one by\n    # installing the above as a subdataset\n    super.install('sub', source=ds.path)\n    # run command that should trigger the demo procedure\n    super.clean()\n    # look for traces\n    ok_file_has_content(op.join(super.path, 'fromproc.txt'), 'hello\\n')\n    ok_clean_git(super.path, index_modified=[op.join('.datalad', 'config')])\n\n\n# FIXME: For some reason fails to commit correctly if on windows and in direct\n# mode. However, direct mode on linux works\n@skip_if(cond=on_windows and cfg.get(\"datalad.repo.version\", None) != 6)\n@with_tree(tree={\n    'code': {'datalad_test_proc.py': \"\"\"\\\nimport sys\nimport os.path as op\nfrom datalad.api import add, Dataset\n\nwith open(op.join(sys.argv[1], 'fromproc.txt'), 'w') as f:\n    f.write('hello\\\\n')\nadd(dataset=Dataset(sys.argv[1]), path='fromproc.txt')\n\"\"\"}})\n@with_tempfile\ndef test_procedure_discovery(path, super_path):\n    ps = run_procedure(discover=True)\n    # there are a few procedures coming with datalad, needs to find them\n    assert_true(len(ps) > 2)\n    # we get three essential properties\n    eq_(\n        sum(['procedure_type' in p and\n             'procedure_callfmt' in p and\n             'path' in p\n             for p in ps]),\n        len(ps))\n\n    # set up dataset with registered procedure (c&p from test_basics):\n    ds = Dataset(path).create(force=True)\n    # TODO: this procedure would leave a clean dataset, but `run` cannot handle dirty\n    # input yet, so manual for now\n    # V6FACT: this leaves the file staged, but not committed\n    ds.add('code', to_git=True)\n    # V6FACT: even this leaves it staged\n    ds.add('.')\n    # V6FACT: but this finally commits it\n    ds.save()\n    # TODO remove above two lines\n    ds.run_procedure('setup_yoda_dataset')\n    ok_clean_git(ds.path)\n    # configure dataset to look for procedures in its code folder\n    ds.config.add(\n        'datalad.locations.dataset-procedures',\n        'code',\n        where='dataset')\n    # configure dataset to run the demo procedure prior to the clean command\n    ds.config.add(\n        'datalad.clean.proc-pre',\n        'datalad_test_proc',\n        where='dataset')\n    ds.add(op.join('.datalad', 'config'))\n\n    # run discovery on the dataset:\n    ps = ds.run_procedure(discover=True)\n\n    # still needs to find procedures coming with datalad\n    assert_true(len(ps) > 2)\n    # we get three essential properties\n    eq_(\n        sum(['procedure_type' in p and\n             'procedure_callfmt' in p and\n             'path' in p\n             for p in ps]),\n        len(ps))\n    # dataset's procedure needs to be in the results\n    assert_in_results(ps, path=op.join(ds.path, 'code', 'datalad_test_proc.py'))\n\n    # make it a subdataset and try again:\n    super = Dataset(super_path).create()\n    super.install('sub', source=ds.path)\n\n    ps = super.run_procedure(discover=True)\n    # still needs to find procedures coming with datalad\n    assert_true(len(ps) > 2)\n    # we get three essential properties\n    eq_(\n        sum(['procedure_type' in p and\n             'procedure_callfmt' in p and\n             'path' in p\n             for p in ps]),\n        len(ps))\n    # dataset's procedure needs to be in the results\n    assert_in_results(ps, path=op.join(super.path, 'sub', 'code',\n                                       'datalad_test_proc.py'))\n\n\n# FIXME: For some reason fails to commit correctly if on windows and in direct\n# mode. However, direct mode on linux works\n@skip_if(cond=on_windows and cfg.get(\"datalad.repo.version\", None) != 6)\n@with_tree(tree={\n    'code': {'datalad_test_proc.py': \"\"\"\\\nimport sys\nimport os.path as op\nfrom datalad.api import add, Dataset\n\nwith open(op.join(sys.argv[1], 'fromproc.txt'), 'w') as f:\n    f.write('{}\\\\n'.format(sys.argv[2]))\nadd(dataset=Dataset(sys.argv[1]), path='fromproc.txt')\n\"\"\"}})\ndef test_configs(path):\n\n    # set up dataset with registered procedure (c&p from test_basics):\n    ds = Dataset(path).create(force=True)\n    # TODO: this procedure would leave a clean dataset, but `run` cannot handle dirty\n    # input yet, so manual for now\n    # V6FACT: this leaves the file staged, but not committed\n    ds.add('code', to_git=True)\n    # V6FACT: even this leaves it staged\n    ds.add('.')\n    # V6FACT: but this finally commits it\n    ds.save()\n    ds.run_procedure('setup_yoda_dataset')\n    ok_clean_git(ds.path)\n    # configure dataset to look for procedures in its code folder\n    ds.config.add(\n        'datalad.locations.dataset-procedures',\n        'code',\n        where='dataset')\n\n    # 1. run procedure based on execution guessing by run_procedure:\n    ds.run_procedure(spec=['datalad_test_proc', 'some_arg'])\n    # look for traces\n    ok_file_has_content(op.join(ds.path, 'fromproc.txt'), 'some_arg\\n')\n\n    # 2. now configure specific call format including usage of substitution config\n    # for run:\n    ds.config.add(\n        'datalad.procedures.datalad_test_proc.call-format',\n        'python \"{script}\" \"{ds}\" {{mysub}} {args}',\n        where='dataset'\n    )\n    ds.config.add(\n        'datalad.run.substitutions.mysub',\n        'dataset-call-config',\n        where='dataset'\n    )\n    # TODO: Should we allow for --inputs/--outputs arguments for run_procedure\n    #       (to be passed into run)?\n    ds.unlock(\"fromproc.txt\")\n    # run again:\n    ds.run_procedure(spec=['datalad_test_proc', 'some_arg'])\n    # look for traces\n    ok_file_has_content(op.join(ds.path, 'fromproc.txt'), 'dataset-call-config\\n')\n\n    # 3. have a conflicting config at user-level, which should override the\n    # config on dataset level:\n    ds.config.add(\n        'datalad.procedures.datalad_test_proc.call-format',\n        'python \"{script}\" \"{ds}\" local {args}',\n        where='local'\n    )\n    ds.unlock(\"fromproc.txt\")\n    # run again:\n    ds.run_procedure(spec=['datalad_test_proc', 'some_arg'])\n    # look for traces\n    ok_file_has_content(op.join(ds.path, 'fromproc.txt'), 'local\\n')\n\n    #\n    ds.config.add(\n        'datalad.procedures.datalad_test_proc.help',\n        \"This is a help message\",\n        where='dataset'\n    )\n\n    r = ds.run_procedure('datalad_test_proc', help_proc=True)\n    assert_true(len(r) == 1)\n    assert_in_results(r, message=\"This is a help message\", status='ok')\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\"\"\"test command datalad run-procedure\n\n\"\"\"\n\n__docformat__ = 'restructuredtext'\n\nimport os.path as op\n\nfrom datalad.tests.utils import ok_clean_git\nfrom datalad.tests.utils import eq_\nfrom datalad.tests.utils import ok_file_has_content\nfrom datalad.tests.utils import with_tree\nfrom datalad.tests.utils import with_tempfile\nfrom datalad.tests.utils import assert_raises\nfrom datalad.tests.utils import assert_true\nfrom datalad.tests.utils import assert_in_results\nfrom datalad.tests.utils import skip_if\nfrom datalad.tests.utils import on_windows\nfrom datalad.distribution.dataset import Dataset\nfrom datalad.support.exceptions import InsufficientArgumentsError\nfrom datalad.api import run_procedure\nfrom datalad.api import clean\nfrom datalad import cfg\n\n\ndef test_invalid_call():\n    # needs spec or discover\n    assert_raises(InsufficientArgumentsError, run_procedure)\n    res = run_procedure('unknown', on_failure='ignore')\n    assert_true(len(res) == 1)\n    assert_in_results(res, status=\"impossible\")\n\n\n# FIXME: For some reason fails to commit correctly if on windows and in direct\n# mode. However, direct mode on linux works\n@skip_if(cond=on_windows and cfg.get(\"datalad.repo.version\", None) != 6)\n@with_tree(tree={\n    'code': {'datalad_test_proc.py': \"\"\"\\\nimport sys\nimport os.path as op\nfrom datalad.api import add, Dataset\n\nwith open(op.join(sys.argv[1], 'fromproc.txt'), 'w') as f:\n    f.write('hello\\\\n')\nadd(dataset=Dataset(sys.argv[1]), path='fromproc.txt')\n\"\"\"}})\n@with_tempfile\ndef test_basics(path, super_path):\n    ds = Dataset(path).create(force=True)\n    # TODO: this procedure would leave a clean dataset, but `run` cannot handle dirty\n    # input yet, so manual for now\n    # V6FACT: this leaves the file staged, but not committed\n    ds.add('code', to_git=True)\n    # V6FACT: even this leaves it staged\n    ds.add('.')\n    # V6FACT: but this finally commits it\n    ds.save()\n    # TODO remove above two lines\n    ds.run_procedure('setup_yoda_dataset')\n    ok_clean_git(ds.path)\n    # configure dataset to look for procedures in its code folder\n    ds.config.add(\n        'datalad.locations.dataset-procedures',\n        'code',\n        where='dataset')\n    # commit this procedure config for later use in a clone:\n    ds.add(op.join('.datalad', 'config'))\n    # configure dataset to run the demo procedure prior to the clean command\n    ds.config.add(\n        'datalad.clean.proc-pre',\n        'datalad_test_proc',\n        where='dataset')\n    # run command that should trigger the demo procedure\n    ds.clean()\n    # look for traces\n    ok_file_has_content(op.join(ds.path, 'fromproc.txt'), 'hello\\n')\n    ok_clean_git(ds.path, index_modified=[op.join('.datalad', 'config')])\n\n    # make a fresh dataset:\n    super = Dataset(super_path).create()\n    # configure dataset to run the demo procedure prior to the clean command\n    super.config.add(\n        'datalad.clean.proc-pre',\n        'datalad_test_proc',\n        where='dataset')\n    # 'super' doesn't know any procedures but should get to know one by\n    # installing the above as a subdataset\n    super.install('sub', source=ds.path)\n    # run command that should trigger the demo procedure\n    super.clean()\n    # look for traces\n    ok_file_has_content(op.join(super.path, 'fromproc.txt'), 'hello\\n')\n    ok_clean_git(super.path, index_modified=[op.join('.datalad', 'config')])\n\n\n# FIXME: For some reason fails to commit correctly if on windows and in direct\n# mode. However, direct mode on linux works\n@skip_if(cond=on_windows and cfg.get(\"datalad.repo.version\", None) != 6)\n@with_tree(tree={\n    'code': {'datalad_test_proc.py': \"\"\"\\\nimport sys\nimport os.path as op\nfrom datalad.api import add, Dataset\n\nwith open(op.join(sys.argv[1], 'fromproc.txt'), 'w') as f:\n    f.write('hello\\\\n')\nadd(dataset=Dataset(sys.argv[1]), path='fromproc.txt')\n\"\"\"}})\n@with_tempfile\ndef test_procedure_discovery(path, super_path):\n    ps = run_procedure(discover=True)\n    # there are a few procedures coming with datalad, needs to find them\n    assert_true(len(ps) > 2)\n    # we get three essential properties\n    eq_(\n        sum(['procedure_type' in p and\n             'procedure_callfmt' in p and\n             'path' in p\n             for p in ps]),\n        len(ps))\n\n    # set up dataset with registered procedure (c&p from test_basics):\n    ds = Dataset(path).create(force=True)\n    # TODO: this procedure would leave a clean dataset, but `run` cannot handle dirty\n    # input yet, so manual for now\n    # V6FACT: this leaves the file staged, but not committed\n    ds.add('code', to_git=True)\n    # V6FACT: even this leaves it staged\n    ds.add('.')\n    # V6FACT: but this finally commits it\n    ds.save()\n    # TODO remove above two lines\n    ds.run_procedure('setup_yoda_dataset')\n    ok_clean_git(ds.path)\n    # configure dataset to look for procedures in its code folder\n    ds.config.add(\n        'datalad.locations.dataset-procedures',\n        'code',\n        where='dataset')\n    # configure dataset to run the demo procedure prior to the clean command\n    ds.config.add(\n        'datalad.clean.proc-pre',\n        'datalad_test_proc',\n        where='dataset')\n    ds.add(op.join('.datalad', 'config'))\n\n    # run discovery on the dataset:\n    ps = ds.run_procedure(discover=True)\n\n    # still needs to find procedures coming with datalad\n    assert_true(len(ps) > 2)\n    # we get three essential properties\n    eq_(\n        sum(['procedure_type' in p and\n             'procedure_callfmt' in p and\n             'path' in p\n             for p in ps]),\n        len(ps))\n    # dataset's procedure needs to be in the results\n    assert_in_results(ps, path=op.join(ds.path, 'code', 'datalad_test_proc.py'))\n\n    # make it a subdataset and try again:\n    super = Dataset(super_path).create()\n    super.install('sub', source=ds.path)\n\n    ps = super.run_procedure(discover=True)\n    # still needs to find procedures coming with datalad\n    assert_true(len(ps) > 2)\n    # we get three essential properties\n    eq_(\n        sum(['procedure_type' in p and\n             'procedure_callfmt' in p and\n             'path' in p\n             for p in ps]),\n        len(ps))\n    # dataset's procedure needs to be in the results\n    assert_in_results(ps, path=op.join(super.path, 'sub', 'code',\n                                       'datalad_test_proc.py'))\n\n\n# FIXME: For some reason fails to commit correctly if on windows and in direct\n# mode. However, direct mode on linux works\n@skip_if(cond=on_windows and cfg.get(\"datalad.repo.version\", None) != 6)\n@with_tree(tree={\n    'code': {'datalad_test_proc.py': \"\"\"\\\nimport sys\nimport os.path as op\nfrom datalad.api import add, Dataset\n\nwith open(op.join(sys.argv[1], 'fromproc.txt'), 'w') as f:\n    f.write('{}\\\\n'.format(sys.argv[2]))\nadd(dataset=Dataset(sys.argv[1]), path='fromproc.txt')\n\"\"\"}})\ndef test_configs(path):\n\n    # set up dataset with registered procedure (c&p from test_basics):\n    ds = Dataset(path).create(force=True)\n    # TODO: this procedure would leave a clean dataset, but `run` cannot handle dirty\n    # input yet, so manual for now\n    # V6FACT: this leaves the file staged, but not committed\n    ds.add('code', to_git=True)\n    # V6FACT: even this leaves it staged\n    ds.add('.')\n    # V6FACT: but this finally commits it\n    ds.save()\n    ds.run_procedure('setup_yoda_dataset')\n    ok_clean_git(ds.path)\n    # configure dataset to look for procedures in its code folder\n    ds.config.add(\n        'datalad.locations.dataset-procedures',\n        'code',\n        where='dataset')\n\n    # 1. run procedure based on execution guessing by run_procedure:\n    ds.run_procedure(spec=['datalad_test_proc', 'some_arg'])\n    # look for traces\n    ok_file_has_content(op.join(ds.path, 'fromproc.txt'), 'some_arg\\n')\n\n    # 2. now configure specific call format including usage of substitution config\n    # for run:\n    ds.config.add(\n        'datalad.procedures.datalad_test_proc.call-format',\n        'python \"{script}\" \"{ds}\" {{mysub}} {args}',\n        where='dataset'\n    )\n    ds.config.add(\n        'datalad.run.substitutions.mysub',\n        'dataset-call-config',\n        where='dataset'\n    )\n    # TODO: Should we allow for --inputs/--outputs arguments for run_procedure\n    #       (to be passed into run)?\n    ds.unlock(\"fromproc.txt\")\n    # run again:\n    ds.run_procedure(spec=['datalad_test_proc', 'some_arg'])\n    # look for traces\n    ok_file_has_content(op.join(ds.path, 'fromproc.txt'), 'dataset-call-config\\n')\n\n    # 3. have a conflicting config at user-level, which should override the\n    # config on dataset level:\n    ds.config.add(\n        'datalad.procedures.datalad_test_proc.call-format',\n        'python \"{script}\" \"{ds}\" local {args}',\n        where='local'\n    )\n    ds.unlock(\"fromproc.txt\")\n    # run again:\n    ds.run_procedure(spec=['datalad_test_proc', 'some_arg'])\n    # look for traces\n    ok_file_has_content(op.join(ds.path, 'fromproc.txt'), 'local\\n')\n\n    # 4. get configured help message:\n    ds.config.add(\n        'datalad.procedures.datalad_test_proc.help',\n        \"This is a help message\",\n        where='dataset'\n    )\n\n    r = ds.run_procedure('datalad_test_proc', help_proc=True)\n    assert_true(len(r) == 1)\n    assert_in_results(r, message=\"This is a help message\", status='ok')\n",
            "header": {
                "file": {
                    "path": "datalad/interface/tests/test_run_procedure.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "7a1a28be9",
                "new_commit_hash": "0a9837908",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/datalad/interface/tests/test_run_procedure.py"
            },
            "plus_file": {
                "path": "b/datalad/interface/tests/test_run_procedure.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 34,
                            "length": 6
                        },
                        "new_range": {
                            "start": 34,
                            "length": 9
                        },
                        "section": "from datalad import cfg"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "def test_invalid_call():",
                                "type": "context"
                            },
                            {
                                "content": "    # needs spec or discover",
                                "type": "context"
                            },
                            {
                                "content": "    assert_raises(InsufficientArgumentsError, run_procedure)",
                                "type": "context"
                            },
                            {
                                "content": "    res = run_procedure('unknown', on_failure='ignore')",
                                "type": "added"
                            },
                            {
                                "content": "    assert_true(len(res) == 1)",
                                "type": "added"
                            },
                            {
                                "content": "    assert_in_results(res, status=\"impossible\")",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "# FIXME: For some reason fails to commit correctly if on windows and in direct",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "datalad/interface/tests/test_run_procedure.py",
                            "type": "function",
                            "name": "test_invalid_call",
                            "content": "def test_invalid_call():\n    # needs spec or discover\n    assert_raises(InsufficientArgumentsError, run_procedure)\n    res = run_procedure('unknown', on_failure='ignore')\n    assert_true(len(res) == 1)\n    assert_in_results(res, status=\"impossible\")",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 34,
                            "end_lineno": 39,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 254,
                            "length": 7
                        },
                        "new_range": {
                            "start": 257,
                            "length": 7
                        },
                        "section": "def test_configs(path):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    # look for traces",
                                "type": "context"
                            },
                            {
                                "content": "    ok_file_has_content(op.join(ds.path, 'fromproc.txt'), 'local\\n')",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    #",
                                "type": "deleted"
                            },
                            {
                                "content": "    # 4. get configured help message:",
                                "type": "added"
                            },
                            {
                                "content": "    ds.config.add(",
                                "type": "context"
                            },
                            {
                                "content": "        'datalad.procedures.datalad_test_proc.help',",
                                "type": "context"
                            },
                            {
                                "content": "        \"This is a help message\",",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "datalad/interface/tests/test_run_procedure.py",
                            "type": "function",
                            "name": "test_configs",
                            "content": "def test_configs(path):\n\n    # set up dataset with registered procedure (c&p from test_basics):\n    ds = Dataset(path).create(force=True)\n    # TODO: this procedure would leave a clean dataset, but `run` cannot handle dirty\n    # input yet, so manual for now\n    # V6FACT: this leaves the file staged, but not committed\n    ds.add('code', to_git=True)\n    # V6FACT: even this leaves it staged\n    ds.add('.')\n    # V6FACT: but this finally commits it\n    ds.save()\n    ds.run_procedure('setup_yoda_dataset')\n    ok_clean_git(ds.path)\n    # configure dataset to look for procedures in its code folder\n    ds.config.add(\n        'datalad.locations.dataset-procedures',\n        'code',\n        where='dataset')\n\n    # 1. run procedure based on execution guessing by run_procedure:\n    ds.run_procedure(spec=['datalad_test_proc', 'some_arg'])\n    # look for traces\n    ok_file_has_content(op.join(ds.path, 'fromproc.txt'), 'some_arg\\n')\n\n    # 2. now configure specific call format including usage of substitution config\n    # for run:\n    ds.config.add(\n        'datalad.procedures.datalad_test_proc.call-format',\n        'python \"{script}\" \"{ds}\" {{mysub}} {args}',\n        where='dataset'\n    )\n    ds.config.add(\n        'datalad.run.substitutions.mysub',\n        'dataset-call-config',\n        where='dataset'\n    )\n    # TODO: Should we allow for --inputs/--outputs arguments for run_procedure\n    #       (to be passed into run)?\n    ds.unlock(\"fromproc.txt\")\n    # run again:\n    ds.run_procedure(spec=['datalad_test_proc', 'some_arg'])\n    # look for traces\n    ok_file_has_content(op.join(ds.path, 'fromproc.txt'), 'dataset-call-config\\n')\n\n    # 3. have a conflicting config at user-level, which should override the\n    # config on dataset level:\n    ds.config.add(\n        'datalad.procedures.datalad_test_proc.call-format',\n        'python \"{script}\" \"{ds}\" local {args}',\n        where='local'\n    )\n    ds.unlock(\"fromproc.txt\")\n    # run again:\n    ds.run_procedure(spec=['datalad_test_proc', 'some_arg'])\n    # look for traces\n    ok_file_has_content(op.join(ds.path, 'fromproc.txt'), 'local\\n')\n\n    #\n    ds.config.add(\n        'datalad.procedures.datalad_test_proc.help',\n        \"This is a help message\",\n        where='dataset'\n    )\n\n    r = ds.run_procedure('datalad_test_proc', help_proc=True)\n    assert_true(len(r) == 1)\n    assert_in_results(r, message=\"This is a help message\", status='ok')",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 199,
                            "end_lineno": 266,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "916ad5273829a0f213d7fd3f098e05477360154d^",
    "new_commit_hash": "916ad5273829a0f213d7fd3f098e05477360154d",
    "commit_message": "Always report procedure type; Yield 'impossible' result instead of raising ValueError",
    "commit_date": "2018-10-11T14:35:37+02:00",
    "metadata": {}
}
{
    "repo_name": "datalad",
    "new_commit_hash": "916ad5273829a0f213d7fd3f098e05477360154d",
    "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\"\"\"test command datalad run-procedure\n\n\"\"\"\n\n__docformat__ = 'restructuredtext'\n\nimport os.path as op\n\nfrom datalad.tests.utils import ok_clean_git\nfrom datalad.tests.utils import eq_\nfrom datalad.tests.utils import ok_file_has_content\nfrom datalad.tests.utils import with_tree\nfrom datalad.tests.utils import with_tempfile\nfrom datalad.tests.utils import assert_raises\nfrom datalad.tests.utils import assert_true\nfrom datalad.tests.utils import assert_in_results\nfrom datalad.tests.utils import skip_if\nfrom datalad.tests.utils import on_windows\nfrom datalad.distribution.dataset import Dataset\nfrom datalad.support.exceptions import InsufficientArgumentsError\nfrom datalad.api import run_procedure\nfrom datalad.api import clean\nfrom datalad import cfg\n\n\ndef test_invalid_call():\n    # needs spec or discover\n    assert_raises(InsufficientArgumentsError, run_procedure)\n    res = run_procedure('unknown', on_failure='ignore')\n    assert_true(len(res) == 1)\n    assert_in_results(res, status=\"impossible\")\n\n\n# FIXME: For some reason fails to commit correctly if on windows and in direct\n# mode. However, direct mode on linux works\n@skip_if(cond=on_windows and cfg.get(\"datalad.repo.version\", None) != 6)\n@with_tree(tree={\n    'code': {'datalad_test_proc.py': \"\"\"\\\nimport sys\nimport os.path as op\nfrom datalad.api import add, Dataset\n\nwith open(op.join(sys.argv[1], 'fromproc.txt'), 'w') as f:\n    f.write('hello\\\\n')\nadd(dataset=Dataset(sys.argv[1]), path='fromproc.txt')\n\"\"\"}})\n@with_tempfile\ndef test_basics(path, super_path):\n    ds = Dataset(path).create(force=True)\n    # TODO: this procedure would leave a clean dataset, but `run` cannot handle dirty\n    # input yet, so manual for now\n    # V6FACT: this leaves the file staged, but not committed\n    ds.add('code', to_git=True)\n    # V6FACT: even this leaves it staged\n    ds.add('.')\n    # V6FACT: but this finally commits it\n    ds.save()\n    # TODO remove above two lines\n    ds.run_procedure('setup_yoda_dataset')\n    ok_clean_git(ds.path)\n    # configure dataset to look for procedures in its code folder\n    ds.config.add(\n        'datalad.locations.dataset-procedures',\n        'code',\n        where='dataset')\n    # commit this procedure config for later use in a clone:\n    ds.add(op.join('.datalad', 'config'))\n    # configure dataset to run the demo procedure prior to the clean command\n    ds.config.add(\n        'datalad.clean.proc-pre',\n        'datalad_test_proc',\n        where='dataset')\n    # run command that should trigger the demo procedure\n    ds.clean()\n    # look for traces\n    ok_file_has_content(op.join(ds.path, 'fromproc.txt'), 'hello\\n')\n    ok_clean_git(ds.path, index_modified=[op.join('.datalad', 'config')])\n\n    # make a fresh dataset:\n    super = Dataset(super_path).create()\n    # configure dataset to run the demo procedure prior to the clean command\n    super.config.add(\n        'datalad.clean.proc-pre',\n        'datalad_test_proc',\n        where='dataset')\n    # 'super' doesn't know any procedures but should get to know one by\n    # installing the above as a subdataset\n    super.install('sub', source=ds.path)\n    # run command that should trigger the demo procedure\n    super.clean()\n    # look for traces\n    ok_file_has_content(op.join(super.path, 'fromproc.txt'), 'hello\\n')\n    ok_clean_git(super.path, index_modified=[op.join('.datalad', 'config')])\n\n\n# FIXME: For some reason fails to commit correctly if on windows and in direct\n# mode. However, direct mode on linux works\n@skip_if(cond=on_windows and cfg.get(\"datalad.repo.version\", None) != 6)\n@with_tree(tree={\n    'code': {'datalad_test_proc.py': \"\"\"\\\nimport sys\nimport os.path as op\nfrom datalad.api import add, Dataset\n\nwith open(op.join(sys.argv[1], 'fromproc.txt'), 'w') as f:\n    f.write('hello\\\\n')\nadd(dataset=Dataset(sys.argv[1]), path='fromproc.txt')\n\"\"\"}})\n@with_tempfile\ndef test_procedure_discovery(path, super_path):\n    ps = run_procedure(discover=True)\n    # there are a few procedures coming with datalad, needs to find them\n    assert_true(len(ps) > 2)\n    # we get three essential properties\n    eq_(\n        sum(['procedure_type' in p and\n             'procedure_callfmt' in p and\n             'path' in p\n             for p in ps]),\n        len(ps))\n\n    # set up dataset with registered procedure (c&p from test_basics):\n    ds = Dataset(path).create(force=True)\n    # TODO: this procedure would leave a clean dataset, but `run` cannot handle dirty\n    # input yet, so manual for now\n    # V6FACT: this leaves the file staged, but not committed\n    ds.add('code', to_git=True)\n    # V6FACT: even this leaves it staged\n    ds.add('.')\n    # V6FACT: but this finally commits it\n    ds.save()\n    # TODO remove above two lines\n    ds.run_procedure('setup_yoda_dataset')\n    ok_clean_git(ds.path)\n    # configure dataset to look for procedures in its code folder\n    ds.config.add(\n        'datalad.locations.dataset-procedures',\n        'code',\n        where='dataset')\n    # configure dataset to run the demo procedure prior to the clean command\n    ds.config.add(\n        'datalad.clean.proc-pre',\n        'datalad_test_proc',\n        where='dataset')\n    ds.add(op.join('.datalad', 'config'))\n\n    # run discovery on the dataset:\n    ps = ds.run_procedure(discover=True)\n\n    # still needs to find procedures coming with datalad\n    assert_true(len(ps) > 2)\n    # we get three essential properties\n    eq_(\n        sum(['procedure_type' in p and\n             'procedure_callfmt' in p and\n             'path' in p\n             for p in ps]),\n        len(ps))\n    # dataset's procedure needs to be in the results\n    assert_in_results(ps, path=op.join(ds.path, 'code', 'datalad_test_proc.py'))\n\n    # make it a subdataset and try again:\n    super = Dataset(super_path).create()\n    super.install('sub', source=ds.path)\n\n    ps = super.run_procedure(discover=True)\n    # still needs to find procedures coming with datalad\n    assert_true(len(ps) > 2)\n    # we get three essential properties\n    eq_(\n        sum(['procedure_type' in p and\n             'procedure_callfmt' in p and\n             'path' in p\n             for p in ps]),\n        len(ps))\n    # dataset's procedure needs to be in the results\n    assert_in_results(ps, path=op.join(super.path, 'sub', 'code',\n                                       'datalad_test_proc.py'))\n\n\n# FIXME: For some reason fails to commit correctly if on windows and in direct\n# mode. However, direct mode on linux works\n@skip_if(cond=on_windows and cfg.get(\"datalad.repo.version\", None) != 6)\n@with_tree(tree={\n    'code': {'datalad_test_proc.py': \"\"\"\\\nimport sys\nimport os.path as op\nfrom datalad.api import add, Dataset\n\nwith open(op.join(sys.argv[1], 'fromproc.txt'), 'w') as f:\n    f.write('{}\\\\n'.format(sys.argv[2]))\nadd(dataset=Dataset(sys.argv[1]), path='fromproc.txt')\n\"\"\"}})\ndef test_configs(path):\n\n    # set up dataset with registered procedure (c&p from test_basics):\n    ds = Dataset(path).create(force=True)\n    # TODO: this procedure would leave a clean dataset, but `run` cannot handle dirty\n    # input yet, so manual for now\n    # V6FACT: this leaves the file staged, but not committed\n    ds.add('code', to_git=True)\n    # V6FACT: even this leaves it staged\n    ds.add('.')\n    # V6FACT: but this finally commits it\n    ds.save()\n    ds.run_procedure('setup_yoda_dataset')\n    ok_clean_git(ds.path)\n    # configure dataset to look for procedures in its code folder\n    ds.config.add(\n        'datalad.locations.dataset-procedures',\n        'code',\n        where='dataset')\n\n    # 1. run procedure based on execution guessing by run_procedure:\n    ds.run_procedure(spec=['datalad_test_proc', 'some_arg'])\n    # look for traces\n    ok_file_has_content(op.join(ds.path, 'fromproc.txt'), 'some_arg\\n')\n\n    # 2. now configure specific call format including usage of substitution config\n    # for run:\n    ds.config.add(\n        'datalad.procedures.datalad_test_proc.call-format',\n        'python \"{script}\" \"{ds}\" {{mysub}} {args}',\n        where='dataset'\n    )\n    ds.config.add(\n        'datalad.run.substitutions.mysub',\n        'dataset-call-config',\n        where='dataset'\n    )\n    # TODO: Should we allow for --inputs/--outputs arguments for run_procedure\n    #       (to be passed into run)?\n    ds.unlock(\"fromproc.txt\")\n    # run again:\n    ds.run_procedure(spec=['datalad_test_proc', 'some_arg'])\n    # look for traces\n    ok_file_has_content(op.join(ds.path, 'fromproc.txt'), 'dataset-call-config\\n')\n\n    # 3. have a conflicting config at user-level, which should override the\n    # config on dataset level:\n    ds.config.add(\n        'datalad.procedures.datalad_test_proc.call-format',\n        'python \"{script}\" \"{ds}\" local {args}',\n        where='local'\n    )\n    ds.unlock(\"fromproc.txt\")\n    # run again:\n    ds.run_procedure(spec=['datalad_test_proc', 'some_arg'])\n    # look for traces\n    ok_file_has_content(op.join(ds.path, 'fromproc.txt'), 'local\\n')\n\n    # 4. get configured help message:\n    ds.config.add(\n        'datalad.procedures.datalad_test_proc.help',\n        \"This is a help message\",\n        where='dataset'\n    )\n\n    r = ds.run_procedure('datalad_test_proc', help_proc=True)\n    assert_true(len(r) == 1)\n    assert_in_results(r, message=\"This is a help message\", status='ok')\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.3.1.dev107\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 87ms\nInstalled 25 packages in 5.64s\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/.tmpJrSEFE/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/.tmpJrSEFE/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/.tmpJrSEFE/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/.tmpJrSEFE/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/.tmpJrSEFE/lib/python3.9/site-packages/setuptools/build_meta.py\", line 320, in run_setup\n    exec(code, locals())\n  File \"<string>\", line 51, 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/.tmpyor4Vj/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/.tmpyor4Vj/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/.tmpyor4Vj/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/.tmpyor4Vj/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/.tmpyor4Vj/lib/python3.9/site-packages/setuptools/build_meta.py\", line 320, in run_setup\n    exec(code, locals())\n  File \"<string>\", line 51, 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_916ad5273829a0f213d7fd3f098e05477360154d/datalad/__init__.py\", line 43, in <module>\n    from datalad.cmd import GitRunner\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_916ad5273829a0f213d7fd3f098e05477360154d/datalad/cmd.py\", line 28, in <module>\n    from .support import path as op\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_916ad5273829a0f213d7fd3f098e05477360154d/datalad/support/path.py\", line 19, in <module>\n    from ..utils import (\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_916ad5273829a0f213d7fd3f098e05477360154d/datalad/utils.py\", line 25, 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 214ms\nInstalled 26 packages in 4.03s\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 57 packages in 1.35s\nPrepared 1 package in 1.33s\nInstalled 50 packages in 87ms\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 + cryptography==44.0.0\n + datalad==0.10.3.1 (from file:///home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_916ad5273829a0f213d7fd3f098e05477360154d)\n + deprecated==1.2.15\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 + 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 90 packages in 205ms\nInstalled 31 packages in 56ms\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 + psutil==6.1.1\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": 1,
    "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_916ad5273829a0f213d7fd3f098e05477360154d\nplugins: cov-4.1.0\ncollected 4 items\n\nr2e_tests/test_1.py .EEF                                                 [100%]\n\n==================================== ERRORS ====================================\n________________________ ERROR at setup of test_basics _________________________\nfile /home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_916ad5273829a0f213d7fd3f098e05477360154d/r2e_tests/test_1.py, line 44\n  @skip_if(cond=on_windows and cfg.get(\"datalad.repo.version\", None) != 6)\n  @with_tree(tree={\n      'code': {'datalad_test_proc.py': \"\"\"\\\n  import sys\n  import os.path as op\n  from datalad.api import add, Dataset\n\n  with open(op.join(sys.argv[1], 'fromproc.txt'), 'w') as f:\n      f.write('hello\\\\n')\n  add(dataset=Dataset(sys.argv[1]), path='fromproc.txt')\n  \"\"\"}})\n  @with_tempfile\n  def test_basics(path, super_path):\nE       fixture 'super_path' not found\n>       available fixtures: cache, capfd, capfdbinary, caplog, capsys, capsysbinary, cov, doctest_namespace, monkeypatch, no_cover, path, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, setup_git_config, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory\n>       use 'pytest --fixtures [testpath]' for help on them.\n\n/home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_916ad5273829a0f213d7fd3f098e05477360154d/r2e_tests/test_1.py:44\n__________________ ERROR at setup of test_procedure_discovery __________________\nfile /home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_916ad5273829a0f213d7fd3f098e05477360154d/r2e_tests/test_1.py, line 106\n  @skip_if(cond=on_windows and cfg.get(\"datalad.repo.version\", None) != 6)\n  @with_tree(tree={\n      'code': {'datalad_test_proc.py': \"\"\"\\\n  import sys\n  import os.path as op\n  from datalad.api import add, Dataset\n\n  with open(op.join(sys.argv[1], 'fromproc.txt'), 'w') as f:\n      f.write('hello\\\\n')\n  add(dataset=Dataset(sys.argv[1]), path='fromproc.txt')\n  \"\"\"}})\n  @with_tempfile\n  def test_procedure_discovery(path, super_path):\nE       fixture 'super_path' not found\n>       available fixtures: cache, capfd, capfdbinary, caplog, capsys, capsysbinary, cov, doctest_namespace, monkeypatch, no_cover, path, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, setup_git_config, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory\n>       use 'pytest --fixtures [testpath]' for help on them.\n\n/home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_916ad5273829a0f213d7fd3f098e05477360154d/r2e_tests/test_1.py:106\n=================================== FAILURES ===================================\n_________________________________ test_configs _________________________________\n\nargs = (), kwargs = {'path': '/tmp/tmp-page2annexxjcw35_1'}\n\n    @wraps(func)\n    def newfunc(*args, **kwargs):\n        if cond:\n            if method == 'raise':\n                raise SkipTest(msg if msg else \"condition was True\")\n            elif method == 'pass':\n                print(msg if msg else \"condition was True\")\n                return\n>       return func(*args, **kwargs)\n\ndatalad/tests/utils.py:860: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\narg = (), kw = {'path': '/tmp/tmp-page2annexxjcw35_1'}\ntkwargs_ = {'prefix': 'datalad_temp_tree_test_configs'}\nd = '/tmp/datalad_temp_tree_test_configsrwxw3xt7'\n\n    @wraps(t)\n    def newfunc(*arg, **kw):\n        tkwargs_ = get_tempfile_kwargs(tkwargs, prefix=\"tree\", wrapped=t)\n        d = tempfile.mkdtemp(**tkwargs_)\n        create_tree(d, tree, archives_leading_dir=archives_leading_dir)\n        try:\n>           return t(*(arg + (d,)), **kw)\nE           TypeError: test_configs() got multiple values for argument 'path'\n\ndatalad/tests/utils.py:418: TypeError\n==================================== PASSES ====================================\n______________________________ test_invalid_call _______________________________\n----------------------------- Captured stderr call -----------------------------\n[WARNING] Cannot find procedure with name 'unknown' [run_procedure(unknown)] \n------------------------------ Captured log call -------------------------------\nWARNING  datalad.interface.run_procedures:utils.py:527 Cannot find procedure with name 'unknown' [run_procedure(unknown)]\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::test_invalid_call\nERROR r2e_tests/test_1.py::test_basics\nERROR r2e_tests/test_1.py::test_procedure_discovery\nFAILED r2e_tests/test_1.py::test_configs - TypeError: test_configs() got mult...\n==================== 1 failed, 1 passed, 2 errors in 1.12s =====================\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_916ad5273829a0f213d7fd3f098e05477360154d\nplugins: cov-4.1.0\ncollected 4 items\n\nr2e_tests/test_1.py FEEF                                                 [100%]\n\n==================================== ERRORS ====================================\n________________________ ERROR at setup of test_basics _________________________\nfile /home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_916ad5273829a0f213d7fd3f098e05477360154d/r2e_tests/test_1.py, line 44\n  @skip_if(cond=on_windows and cfg.get(\"datalad.repo.version\", None) != 6)\n  @with_tree(tree={\n      'code': {'datalad_test_proc.py': \"\"\"\\\n  import sys\n  import os.path as op\n  from datalad.api import add, Dataset\n\n  with open(op.join(sys.argv[1], 'fromproc.txt'), 'w') as f:\n      f.write('hello\\\\n')\n  add(dataset=Dataset(sys.argv[1]), path='fromproc.txt')\n  \"\"\"}})\n  @with_tempfile\n  def test_basics(path, super_path):\nE       fixture 'super_path' not found\n>       available fixtures: cache, capfd, capfdbinary, caplog, capsys, capsysbinary, cov, doctest_namespace, monkeypatch, no_cover, path, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, setup_git_config, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory\n>       use 'pytest --fixtures [testpath]' for help on them.\n\n/home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_916ad5273829a0f213d7fd3f098e05477360154d/r2e_tests/test_1.py:44\n__________________ ERROR at setup of test_procedure_discovery __________________\nfile /home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_916ad5273829a0f213d7fd3f098e05477360154d/r2e_tests/test_1.py, line 106\n  @skip_if(cond=on_windows and cfg.get(\"datalad.repo.version\", None) != 6)\n  @with_tree(tree={\n      'code': {'datalad_test_proc.py': \"\"\"\\\n  import sys\n  import os.path as op\n  from datalad.api import add, Dataset\n\n  with open(op.join(sys.argv[1], 'fromproc.txt'), 'w') as f:\n      f.write('hello\\\\n')\n  add(dataset=Dataset(sys.argv[1]), path='fromproc.txt')\n  \"\"\"}})\n  @with_tempfile\n  def test_procedure_discovery(path, super_path):\nE       fixture 'super_path' not found\n>       available fixtures: cache, capfd, capfdbinary, caplog, capsys, capsysbinary, cov, doctest_namespace, monkeypatch, no_cover, path, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, setup_git_config, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory\n>       use 'pytest --fixtures [testpath]' for help on them.\n\n/home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_916ad5273829a0f213d7fd3f098e05477360154d/r2e_tests/test_1.py:106\n=================================== FAILURES ===================================\n______________________________ test_invalid_call _______________________________\n\nspec = ['unknown'], dataset = None, discover = False, help_proc = False\n\n    @staticmethod\n    @datasetmethod(name='run_procedure')\n    @eval_results\n    def __call__(\n            spec=None,\n            dataset=None,\n            discover=False,\n            help_proc=False):\n        if not spec and not discover:\n            raise InsufficientArgumentsError('requires at least a procedure name')\n        if help_proc and not spec:\n            raise InsufficientArgumentsError('requires a procedure name')\n    \n        try:\n            ds = require_dataset(\n                dataset, check_installed=False,\n                purpose='run a procedure')\n        except NoDatasetArgumentFound:\n            ds = None\n    \n        if discover:\n            reported = set()\n            for m, cmd_tmpl, cmd_help in _get_procedure_implementation('*', ds=ds):\n                if m in reported:\n                    continue\n                if not cmd_tmpl:\n                    cmd_type, cmd_tmpl = _guess_exec(m)\n                if cmd_type is None and cmd_tmpl is None:\n                    # doesn't seem like a match\n                    continue\n                res = get_status_dict(\n                    action='run_procedure',\n                    path=m,\n                    type='file',\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='ok',\n                    procedure_type=cmd_type,\n                    procedure_callfmt=cmd_tmpl,\n                    message=cmd_type)\n                reported.add(m)\n                yield res\n            return\n    \n        if not isinstance(spec, (tuple, list)):\n            # maybe coming from config\n            import shlex\n            spec = shlex.split(spec)\n        name = spec[0]\n        args = spec[1:]\n    \n        try:\n            # get the first match an run with it\n            procedure_file, cmd_tmpl, cmd_help = \\\n>               next(_get_procedure_implementation(name, ds=ds))\nE               StopIteration\n\ndatalad/interface/run_procedure.py:296: StopIteration\n\nDuring handling of the above exception, another exception occurred:\n\n    def test_invalid_call():\n        # needs spec or discover\n        assert_raises(InsufficientArgumentsError, run_procedure)\n>       res = run_procedure('unknown', on_failure='ignore')\n\nr2e_tests/test_1.py:37: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \ndatalad/interface/utils.py:479: in eval_func\n    return return_func(generator_func)(*args, **kwargs)\ndatalad/interface/utils.py:467: in return_func\n    results = list(results)\ndatalad/interface/utils.py:422: in generator_func\n    result_renderer, result_xfm, _result_filter, **_kwargs):\ndatalad/interface/utils.py:491: in _process_results\n    for res in results:\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nspec = ['unknown'], dataset = None, discover = False, help_proc = False\n\n    @staticmethod\n    @datasetmethod(name='run_procedure')\n    @eval_results\n    def __call__(\n            spec=None,\n            dataset=None,\n            discover=False,\n            help_proc=False):\n        if not spec and not discover:\n            raise InsufficientArgumentsError('requires at least a procedure name')\n        if help_proc and not spec:\n            raise InsufficientArgumentsError('requires a procedure name')\n    \n        try:\n            ds = require_dataset(\n                dataset, check_installed=False,\n                purpose='run a procedure')\n        except NoDatasetArgumentFound:\n            ds = None\n    \n        if discover:\n            reported = set()\n            for m, cmd_tmpl, cmd_help in _get_procedure_implementation('*', ds=ds):\n                if m in reported:\n                    continue\n                if not cmd_tmpl:\n                    cmd_type, cmd_tmpl = _guess_exec(m)\n                if cmd_type is None and cmd_tmpl is None:\n                    # doesn't seem like a match\n                    continue\n                res = get_status_dict(\n                    action='run_procedure',\n                    path=m,\n                    type='file',\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='ok',\n                    procedure_type=cmd_type,\n                    procedure_callfmt=cmd_tmpl,\n                    message=cmd_type)\n                reported.add(m)\n                yield res\n            return\n    \n        if not isinstance(spec, (tuple, list)):\n            # maybe coming from config\n            import shlex\n            spec = shlex.split(spec)\n        name = spec[0]\n        args = spec[1:]\n    \n        try:\n            # get the first match an run with it\n            procedure_file, cmd_tmpl, cmd_help = \\\n                next(_get_procedure_implementation(name, ds=ds))\n        except StopIteration:\n            # TODO error result\n>           raise ValueError(\"Cannot find procedure with name '%s'\", name)\nE           ValueError: (\"Cannot find procedure with name '%s'\", 'unknown')\n\ndatalad/interface/run_procedure.py:299: ValueError\n_________________________________ test_configs _________________________________\n\nargs = (), kwargs = {'path': '/tmp/tmp-page2annex4n_428cv'}\n\n    @wraps(func)\n    def newfunc(*args, **kwargs):\n        if cond:\n            if method == 'raise':\n                raise SkipTest(msg if msg else \"condition was True\")\n            elif method == 'pass':\n                print(msg if msg else \"condition was True\")\n                return\n>       return func(*args, **kwargs)\n\ndatalad/tests/utils.py:860: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\narg = (), kw = {'path': '/tmp/tmp-page2annex4n_428cv'}\ntkwargs_ = {'prefix': 'datalad_temp_tree_test_configs'}\nd = '/tmp/datalad_temp_tree_test_configs79vi90u6'\n\n    @wraps(t)\n    def newfunc(*arg, **kw):\n        tkwargs_ = get_tempfile_kwargs(tkwargs, prefix=\"tree\", wrapped=t)\n        d = tempfile.mkdtemp(**tkwargs_)\n        create_tree(d, tree, archives_leading_dir=archives_leading_dir)\n        try:\n>           return t(*(arg + (d,)), **kw)\nE           TypeError: test_configs() got multiple values for argument 'path'\n\ndatalad/tests/utils.py:418: TypeError\n=========================== short test summary info ============================\nERROR r2e_tests/test_1.py::test_basics\nERROR r2e_tests/test_1.py::test_procedure_discovery\nFAILED r2e_tests/test_1.py::test_invalid_call - ValueError: (\"Cannot find pro...\nFAILED r2e_tests/test_1.py::test_configs - TypeError: test_configs() got mult...\n========================= 2 failed, 2 errors in 0.70s ==========================\n",
    "old_commit_res_stderr": ""
}
datalad/interface/run_procedure.py datalad/interface/tests/test_run_procedure.py
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** `run_procedure` Raises TypeError Due to Multiple `path` Arguments

**Description:**

After recent changes, invoking `run_procedure` with certain parameters results in a `TypeError` because multiple `path` arguments are being passed unintentionally.

**Reproduction Steps:**

```python
res = run_procedure('unknown', on_failure='ignore')
```

**Expected Behavior:**

The `run_procedure` function should handle the case where an unknown procedure name is provided gracefully by yielding a result with a status of `"impossible"` without raising any exceptions.

**Actual Behavior:**

A `TypeError` is raised indicating that multiple values for the `path` argument are being passed:

```
TypeError: test_configs() got multiple values for argument 'path'
```

This prevents the function from handling unknown procedures as intended and disrupts the workflow.
--- 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/run_procedure.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.
#
# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
"""Run prepared procedures (DataLad scripts) on a dataset"""

__docformat__ = 'restructuredtext'


import logging

from glob import iglob
from argparse import REMAINDER
import os
import os.path as op
import stat

from datalad import cfg
from datalad.interface.base import Interface
from datalad.interface.utils import eval_results
from datalad.interface.base import build_doc
from datalad.interface.results import get_status_dict

from datalad.distribution.dataset import Dataset
from datalad.distribution.dataset import require_dataset
from datalad.distribution.dataset import EnsureDataset
from datalad.support.constraints import EnsureNone
from datalad.support.param import Parameter
from datalad.distribution.dataset import datasetmethod
from datalad.support.exceptions import InsufficientArgumentsError
from datalad.support.exceptions import NoDatasetArgumentFound

from datalad.utils import assure_list

# bound dataset methods
from datalad.interface.run import Run
from datalad.distribution.subdatasets import Subdatasets

lgr = logging.getLogger('datalad.interface.run_procedures')


def _get_file_match(dir, name='*'):
    targets = (name, ('[!_]*.py'), ('[!_]*.sh'))
    lgr.debug("Looking for procedure '%s' in '%s'", name, dir)
    for target in targets:
        for m in iglob(op.join(dir, target)):
            m_bn = op.basename(m)
            if name == '*':
                report_name = m_bn[:-3] if m_bn.endswith('.py') or \
                                           m_bn.endswith('.sh') \
                                        else m_bn
                yield m, report_name
            elif m_bn == name or m_bn.startswith('{}.'.format(name)):
                yield m, name


def _get_proc_config(name, ds=None):
    """get configuration of named procedure

    Returns
    -------
    tuple
      (call format string, help string) or possibly None for either value,
      if there's nothing configured
    """
    # figure what ConfigManager to ask
    cm = cfg if ds is None else ds.config
    v = cm.get('datalad.procedures.{}.call-format'.format(name), None)
    h = cm.get('datalad.procedures.{}.help'.format(name), None)
    if isinstance(v, tuple):
        # If multiple values are defined (at the same level), ConfigManager
        # returns a tuple. Go with the last one.
        # TODO: Should we issue a warning or fail entirely?
        return v[-1], h
    else:
        return v, h


def _get_procedure_implementation(name='*', ds=None):
    ds = ds if isinstance(ds, Dataset) else Dataset(ds) if ds else None

    # 1. check system and user account for procedure
    for loc in (cfg.obtain('datalad.locations.user-procedures'),
                cfg.obtain('datalad.locations.system-procedures')):
        for dir in assure_list(loc):
            for m, n in _get_file_match(dir, name):
                yield (m,) + _get_proc_config(n)
    # 2. check dataset for procedure
    if ds is not None and ds.is_installed():
        # could be more than one
        dirs = assure_list(
                ds.config.obtain('datalad.locations.dataset-procedures'))
        for dir in dirs:
            # TODO `get` dirs if necessary
            for m, n in _get_file_match(op.join(ds.path, dir), name):
                yield (m,) + _get_proc_config(n, ds=ds)
        # 1.1. check subdatasets recursively
        for subds in ds.subdatasets(return_type='generator',
                                    result_xfm='datasets'):
            for m, f, h in _get_procedure_implementation(name=name, ds=subds):
                yield m, f, h

    # 3. check extensions for procedure
    # delay heavy import until here
    from pkg_resources import iter_entry_points
    from pkg_resources import resource_isdir
    from pkg_resources import resource_filename
    for entry_point in iter_entry_points('datalad.extensions'):
        # use of '/' here is OK wrt to platform compatibility
        if resource_isdir(entry_point.module_name, 'resources/procedures'):
            for m, n in _get_file_match(
                    resource_filename(
                        entry_point.module_name,
                        'resources/procedures'),
                    name):
                yield (m,) + _get_proc_config(n)
    # 4. at last check datalad itself for procedure
    for m, n in _get_file_match(
            resource_filename('datalad', 'resources/procedures'),
            name):
        yield (m,) + _get_proc_config(n)


def _guess_exec(script_file):
    # TODO check for exec permission and rely on interpreter
    if os.stat(script_file).st_mode & stat.S_IEXEC:
        return ('executable', u'"{script}" "{ds}" {args}')
    elif script_file.endswith('.sh'):
        return (u'bash_script', u'bash "{script}" "{ds}" {args}')
    elif script_file.endswith('.py'):
        return (u'python_script', u'python "{script}" "{ds}" {args}')
    else:
        return None, None


@build_doc
class RunProcedure(Interface):
    """Run prepared procedures (DataLad scripts) on a dataset

    *Concept*

    A "procedure" is an algorithm with the purpose to process a dataset in a
    particular way. Procedures can be useful in a wide range of scenarios,
    like adjusting dataset configuration in a uniform fashion, populating
    a dataset with particular content, or automating other routine tasks,
    such as synchronizing dataset content with certain siblings.

    Implementations of some procedures are shipped together with DataLad,
    but additional procedures can be provided by 1) any DataLad extension,
    2) any dataset, 3) a local user, or 4) a local system administrator.
    DataLad will look for procedures in the following locations and order:

    Directories identified by the configuration settings

    - 'datalad.locations.dataset-procedures'
    - 'datalad.locations.user-procedures' (determined by
      appdirs.user_config_dir; defaults to '$HOME/.config/datalad/procedures'
      on GNU/Linux systems)
    - 'datalad.locations.system-procedures' (determined by
      appdirs.site_config_dir; defaults to '/etc/xdg/datalad/procedures' on
      GNU/Linux systems)

    and subsequently in the 'resources/procedures/' directories of any
    installed extension, and, lastly, of the DataLad installation itself.

    Each configuration setting can occur multiple times to indicate multiple
    directories to be searched. If a procedure matching a given name is found
    (filename without a possible extension), the search is aborted and this
    implementation will be executed. This makes it possible for individual
    datasets, users, or machines to override externally provided procedures
    (enabling the implementation of customizable processing "hooks").


    *Procedure implementation*

    A procedure can be any executable. Executables must have the appropriate
    permissions and, in the case of a script, must contain an appropriate
    "shebang" line. If a procedure is not executable, but its filename ends
    with '.py', it is automatically executed by the 'python' interpreter
    (whichever version is available in the present environment). Likewise,
    procedure implementations ending on '.sh' are executed via 'bash'.

    Procedures can implement any argument handling, but must be capable
    of taking at least one positional argument (the absolute path to the
    dataset they shall operate on).


    *Customize other commands with procedures*

    On execution of any commands, DataLad inspects two additional
    configuration settings:

    - 'datalad.<name>.proc-pre'

    - 'datalad.<name>.proc-post'

    where '<name>' is the name of a DataLad command. Using this mechanism
    DataLad can be instructed to run one or more procedures before or
    after the execution of a given command. For example, configuring
    a set of metadata types in any newly created dataset can be achieved
    via:

      % datalad -c 'datalad.create.proc-post=cfg_metadatatypes xmp image' create -d myds

    As procedures run on datasets, it is necessary to explicitly identify
    the target dataset via the -d (--dataset) option.
    """
    _params_ = dict(
        spec=Parameter(
            args=("spec",),
            metavar='NAME [ARGS]',
            nargs=REMAINDER,
            doc="""Name and possibly additional arguments of the
            to-be-executed procedure."""),
        dataset=Parameter(
            args=("-d", "--dataset"),
            metavar="PATH",
            doc="""specify the dataset to run the procedure on.
            An attempt is made to identify the dataset based on the current
            working directory.""",
            constraints=EnsureDataset() | EnsureNone()),
        discover=Parameter(
            args=('--discover',),
            action='store_true',
            doc="""if given, all configured paths are searched for procedures
            and one result record per discovered procedure is yielded, but
            no procedure is executed"""),
        help_proc=Parameter(
            args=('--help-proc',),
            action='store_true',
            doc="""if given, get a help message for procedure NAME from config
            setting datalad.procedures.NAME.help. [CMD: Note, that this 
            parameter needs to be given before NAME, since it would be regarded 
            as part of ARGS otherwise CMD]"""
        )
    )

    @staticmethod
    @datasetmethod(name='run_procedure')
    @eval_results
    def __call__(
            spec=None,
            dataset=None,
            discover=False,
            help_proc=False):
        if not spec and not discover:
            raise InsufficientArgumentsError('requires at least a procedure name')
        if help_proc and not spec:
            raise InsufficientArgumentsError('requires a procedure name')

        try:
            ds = require_dataset(
                dataset, check_installed=False,
                purpose='run a procedure')
        except NoDatasetArgumentFound:
            ds = None

        if discover:
            reported = set()
            for m, cmd_tmpl, cmd_help in _get_procedure_implementation('*', ds=ds):
                if m in reported:
                    continue
                if not cmd_tmpl:
                    cmd_type, cmd_tmpl = _guess_exec(m)
                if cmd_type is None and cmd_tmpl is None:
                    # doesn't seem like a match
                    continue
                res = get_status_dict(
                    action='run_procedure',
                    path=m,
                    type='file',
                    logger=lgr,
                    refds=ds.path if ds else None,
                    status='ok',
                    procedure_type=cmd_type,
                    procedure_callfmt=cmd_tmpl,
                    message=cmd_type)
                reported.add(m)
                yield res
            return

        if not isinstance(spec, (tuple, list)):
            # maybe coming from config
            import shlex
            spec = shlex.split(spec)
        name = spec[0]
        args = spec[1:]

        try:
            # get the first match an run with it
            procedure_file, cmd_tmpl, cmd_help = \
                next(_get_procedure_implementation(name, ds=ds))
        except StopIteration:
            # TODO error result
            raise ValueError("Cannot find procedure with name '%s'", name)

        if not cmd_tmpl:
            cmd_type, cmd_tmpl = _guess_exec(procedure_file)
            if cmd_tmpl is None:
                raise ValueError("No idea how to execute procedure %s. "
                                 "Missing 'execute' permissions?",
                                 procedure_file)

        if help_proc:
            res = get_status_dict(
                    action='procedure_help',
                    path=procedure_file,
                    type='file',
                    logger=lgr,
                    refds=ds.path if ds else None,
                    status='ok',
                    procedure_callfmt=cmd_tmpl,
                    message=cmd_help)
            yield res
            return

        cmd = cmd_tmpl.format(
            script=procedure_file,
            ds=ds.path if ds else '',
            args=u' '.join(u'"{}"'.format(a) for a in args) if args else '')
        lgr.debug('Attempt to run procedure {} as: {}'.format(
            name,
            cmd))
        for r in Run.__call__(
                cmd=cmd,
                dataset=ds,
                explicit=True,
                inputs=None,
                outputs=None,
                # pass through here
                on_failure='ignore',
        ):
            yield r

```
--- 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:** `run_procedure` Raises TypeError Due to Multiple `path` Arguments

**Description:**

After recent changes, invoking `run_procedure` with certain parameters results in a `TypeError` because multiple `path` arguments are being passed unintentionally.

**Reproduction Steps:**

```python
res = run_procedure('unknown', on_failure='ignore')
```

**Expected Behavior:**

The `run_procedure` function should handle the case where an unknown procedure name is provided gracefully by yielding a result with a status of `"impossible"` without raising any exceptions.

**Actual Behavior:**

A `TypeError` is raised indicating that multiple values for the `path` argument are being passed:

```
TypeError: test_configs() got multiple values for argument 'path'
```

This prevents the function from handling unknown procedures as intended and disrupts the workflow.
{
    "test_invalid_call": "PASSED",
    "test_basics": "ERROR",
    "test_procedure_discovery": "ERROR",
    "test_configs": "FAILED"
}
diff --git a/datalad/interface/run_procedure.py b/datalad/interface/run_procedure.py
index 1ea343927..6ed01fa1f 100644
--- a/datalad/interface/run_procedure.py
+++ b/datalad/interface/run_procedure.py
@@ -264,8 +264,9 @@ class RunProcedure(Interface):
             for m, cmd_tmpl, cmd_help in _get_procedure_implementation('*', ds=ds):
                 if m in reported:
                     continue
+                cmd_type, cmd_tmpl_g = _guess_exec(m)
                 if not cmd_tmpl:
-                    cmd_type, cmd_tmpl = _guess_exec(m)
+                    cmd_tmpl = cmd_tmpl_g
                 if cmd_type is None and cmd_tmpl is None:
                     # doesn't seem like a match
                     continue
@@ -295,15 +296,25 @@ class RunProcedure(Interface):
             procedure_file, cmd_tmpl, cmd_help = \
                 next(_get_procedure_implementation(name, ds=ds))
         except StopIteration:
-            # TODO error result
-            raise ValueError("Cannot find procedure with name '%s'", name)
+            res = get_status_dict(
+                    action='run_procedure',
+                    # TODO: Default renderer requires a key "path" to exist.
+                    # Doesn't make a lot of sense in this case
+                    path=name,
+                    logger=lgr,
+                    refds=ds.path if ds else None,
+                    status='impossible',
+                    message="Cannot find procedure with name '%s'" % name)
+            yield res
+            return
 
+        cmd_type, cmd_tmpl_g = _guess_exec(procedure_file)
         if not cmd_tmpl:
-            cmd_type, cmd_tmpl = _guess_exec(procedure_file)
-            if cmd_tmpl is None:
-                raise ValueError("No idea how to execute procedure %s. "
-                                 "Missing 'execute' permissions?",
-                                 procedure_file)
+            cmd_tmpl = cmd_tmpl_g
+        if not cmd_tmpl:
+            raise ValueError("No idea how to execute procedure %s. "
+                             "Missing 'execute' permissions?",
+                             procedure_file)
 
         if help_proc:
             res = get_status_dict(
@@ -313,6 +324,7 @@ class RunProcedure(Interface):
                     logger=lgr,
                     refds=ds.path if ds else None,
                     status='ok',
+                    procedure_type=cmd_type,
                     procedure_callfmt=cmd_tmpl,
                     message=cmd_help)
             yield res

{"datalad/interface/run_procedure.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\"\"\"Run prepared procedures (DataLad scripts) on a dataset\"\"\"\n\n__docformat__ = 'restructuredtext'\n\n\nimport logging\n\nfrom glob import iglob\nfrom argparse import REMAINDER\nimport os\nimport os.path as op\nimport stat\n\nfrom datalad import cfg\nfrom datalad.interface.base import Interface\nfrom datalad.interface.utils import eval_results\nfrom datalad.interface.base import build_doc\nfrom datalad.interface.results import get_status_dict\n\nfrom datalad.distribution.dataset import Dataset\nfrom datalad.distribution.dataset import require_dataset\nfrom datalad.distribution.dataset import EnsureDataset\nfrom datalad.support.constraints import EnsureNone\nfrom datalad.support.param import Parameter\nfrom datalad.distribution.dataset import datasetmethod\nfrom datalad.support.exceptions import InsufficientArgumentsError\nfrom datalad.support.exceptions import NoDatasetArgumentFound\n\nfrom datalad.utils import assure_list\n\n# bound dataset methods\nfrom datalad.interface.run import Run\nfrom datalad.distribution.subdatasets import Subdatasets\n\nlgr = logging.getLogger('datalad.interface.run_procedures')\n\n\ndef _get_file_match(dir, name='*'):\n    targets = (name, ('[!_]*.py'), ('[!_]*.sh'))\n    lgr.debug(\"Looking for procedure '%s' in '%s'\", name, dir)\n    for target in targets:\n        for m in iglob(op.join(dir, target)):\n            m_bn = op.basename(m)\n            if name == '*':\n                report_name = m_bn[:-3] if m_bn.endswith('.py') or \\\n                                           m_bn.endswith('.sh') \\\n                                        else m_bn\n                yield m, report_name\n            elif m_bn == name or m_bn.startswith('{}.'.format(name)):\n                yield m, name\n\n\ndef _get_proc_config(name, ds=None):\n    \"\"\"get configuration of named procedure\n\n    Returns\n    -------\n    tuple\n      (call format string, help string) or possibly None for either value,\n      if there's nothing configured\n    \"\"\"\n    # figure what ConfigManager to ask\n    cm = cfg if ds is None else ds.config\n    v = cm.get('datalad.procedures.{}.call-format'.format(name), None)\n    h = cm.get('datalad.procedures.{}.help'.format(name), None)\n    if isinstance(v, tuple):\n        # If multiple values are defined (at the same level), ConfigManager\n        # returns a tuple. Go with the last one.\n        # TODO: Should we issue a warning or fail entirely?\n        return v[-1], h\n    else:\n        return v, h\n\n\ndef _get_procedure_implementation(name='*', ds=None):\n    ds = ds if isinstance(ds, Dataset) else Dataset(ds) if ds else None\n\n    # 1. check system and user account for procedure\n    for loc in (cfg.obtain('datalad.locations.user-procedures'),\n                cfg.obtain('datalad.locations.system-procedures')):\n        for dir in assure_list(loc):\n            for m, n in _get_file_match(dir, name):\n                yield (m,) + _get_proc_config(n)\n    # 2. check dataset for procedure\n    if ds is not None and ds.is_installed():\n        # could be more than one\n        dirs = assure_list(\n                ds.config.obtain('datalad.locations.dataset-procedures'))\n        for dir in dirs:\n            # TODO `get` dirs if necessary\n            for m, n in _get_file_match(op.join(ds.path, dir), name):\n                yield (m,) + _get_proc_config(n, ds=ds)\n        # 1.1. check subdatasets recursively\n        for subds in ds.subdatasets(return_type='generator',\n                                    result_xfm='datasets'):\n            for m, f, h in _get_procedure_implementation(name=name, ds=subds):\n                yield m, f, h\n\n    # 3. check extensions for procedure\n    # delay heavy import until here\n    from pkg_resources import iter_entry_points\n    from pkg_resources import resource_isdir\n    from pkg_resources import resource_filename\n    for entry_point in iter_entry_points('datalad.extensions'):\n        # use of '/' here is OK wrt to platform compatibility\n        if resource_isdir(entry_point.module_name, 'resources/procedures'):\n            for m, n in _get_file_match(\n                    resource_filename(\n                        entry_point.module_name,\n                        'resources/procedures'),\n                    name):\n                yield (m,) + _get_proc_config(n)\n    # 4. at last check datalad itself for procedure\n    for m, n in _get_file_match(\n            resource_filename('datalad', 'resources/procedures'),\n            name):\n        yield (m,) + _get_proc_config(n)\n\n\ndef _guess_exec(script_file):\n    # TODO check for exec permission and rely on interpreter\n    if os.stat(script_file).st_mode & stat.S_IEXEC:\n        return ('executable', u'\"{script}\" \"{ds}\" {args}')\n    elif script_file.endswith('.sh'):\n        return (u'bash_script', u'bash \"{script}\" \"{ds}\" {args}')\n    elif script_file.endswith('.py'):\n        return (u'python_script', u'python \"{script}\" \"{ds}\" {args}')\n    else:\n        return None, None\n\n\n@build_doc\nclass RunProcedure(Interface):\n    \"\"\"Run prepared procedures (DataLad scripts) on a dataset\n\n    *Concept*\n\n    A \"procedure\" is an algorithm with the purpose to process a dataset in a\n    particular way. Procedures can be useful in a wide range of scenarios,\n    like adjusting dataset configuration in a uniform fashion, populating\n    a dataset with particular content, or automating other routine tasks,\n    such as synchronizing dataset content with certain siblings.\n\n    Implementations of some procedures are shipped together with DataLad,\n    but additional procedures can be provided by 1) any DataLad extension,\n    2) any dataset, 3) a local user, or 4) a local system administrator.\n    DataLad will look for procedures in the following locations and order:\n\n    Directories identified by the configuration settings\n\n    - 'datalad.locations.dataset-procedures'\n    - 'datalad.locations.user-procedures' (determined by\n      appdirs.user_config_dir; defaults to '$HOME/.config/datalad/procedures'\n      on GNU/Linux systems)\n    - 'datalad.locations.system-procedures' (determined by\n      appdirs.site_config_dir; defaults to '/etc/xdg/datalad/procedures' on\n      GNU/Linux systems)\n\n    and subsequently in the 'resources/procedures/' directories of any\n    installed extension, and, lastly, of the DataLad installation itself.\n\n    Each configuration setting can occur multiple times to indicate multiple\n    directories to be searched. If a procedure matching a given name is found\n    (filename without a possible extension), the search is aborted and this\n    implementation will be executed. This makes it possible for individual\n    datasets, users, or machines to override externally provided procedures\n    (enabling the implementation of customizable processing \"hooks\").\n\n\n    *Procedure implementation*\n\n    A procedure can be any executable. Executables must have the appropriate\n    permissions and, in the case of a script, must contain an appropriate\n    \"shebang\" line. If a procedure is not executable, but its filename ends\n    with '.py', it is automatically executed by the 'python' interpreter\n    (whichever version is available in the present environment). Likewise,\n    procedure implementations ending on '.sh' are executed via 'bash'.\n\n    Procedures can implement any argument handling, but must be capable\n    of taking at least one positional argument (the absolute path to the\n    dataset they shall operate on).\n\n\n    *Customize other commands with procedures*\n\n    On execution of any commands, DataLad inspects two additional\n    configuration settings:\n\n    - 'datalad.<name>.proc-pre'\n\n    - 'datalad.<name>.proc-post'\n\n    where '<name>' is the name of a DataLad command. Using this mechanism\n    DataLad can be instructed to run one or more procedures before or\n    after the execution of a given command. For example, configuring\n    a set of metadata types in any newly created dataset can be achieved\n    via:\n\n      % datalad -c 'datalad.create.proc-post=cfg_metadatatypes xmp image' create -d myds\n\n    As procedures run on datasets, it is necessary to explicitly identify\n    the target dataset via the -d (--dataset) option.\n    \"\"\"\n    _params_ = dict(\n        spec=Parameter(\n            args=(\"spec\",),\n            metavar='NAME [ARGS]',\n            nargs=REMAINDER,\n            doc=\"\"\"Name and possibly additional arguments of the\n            to-be-executed procedure.\"\"\"),\n        dataset=Parameter(\n            args=(\"-d\", \"--dataset\"),\n            metavar=\"PATH\",\n            doc=\"\"\"specify the dataset to run the procedure on.\n            An attempt is made to identify the dataset based on the current\n            working directory.\"\"\",\n            constraints=EnsureDataset() | EnsureNone()),\n        discover=Parameter(\n            args=('--discover',),\n            action='store_true',\n            doc=\"\"\"if given, all configured paths are searched for procedures\n            and one result record per discovered procedure is yielded, but\n            no procedure is executed\"\"\"),\n        help_proc=Parameter(\n            args=('--help-proc',),\n            action='store_true',\n            doc=\"\"\"if given, get a help message for procedure NAME from config\n            setting datalad.procedures.NAME.help. [CMD: Note, that this \n            parameter needs to be given before NAME, since it would be regarded \n            as part of ARGS otherwise CMD]\"\"\"\n        )\n    )\n\n    @staticmethod\n    @datasetmethod(name='run_procedure')\n    @eval_results\n    def __call__(\n            spec=None,\n            dataset=None,\n            discover=False,\n            help_proc=False):\n        if not spec and not discover:\n            raise InsufficientArgumentsError('requires at least a procedure name')\n        if help_proc and not spec:\n            raise InsufficientArgumentsError('requires a procedure name')\n\n        try:\n            ds = require_dataset(\n                dataset, check_installed=False,\n                purpose='run a procedure')\n        except NoDatasetArgumentFound:\n            ds = None\n\n        if discover:\n            reported = set()\n            for m, cmd_tmpl, cmd_help in _get_procedure_implementation('*', ds=ds):\n                if m in reported:\n                    continue\n                if not cmd_tmpl:\n                    cmd_type, cmd_tmpl = _guess_exec(m)\n                if cmd_type is None and cmd_tmpl is None:\n                    # doesn't seem like a match\n                    continue\n                res = get_status_dict(\n                    action='run_procedure',\n                    path=m,\n                    type='file',\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='ok',\n                    procedure_type=cmd_type,\n                    procedure_callfmt=cmd_tmpl,\n                    message=cmd_type)\n                reported.add(m)\n                yield res\n            return\n\n        if not isinstance(spec, (tuple, list)):\n            # maybe coming from config\n            import shlex\n            spec = shlex.split(spec)\n        name = spec[0]\n        args = spec[1:]\n\n        try:\n            # get the first match an run with it\n            procedure_file, cmd_tmpl, cmd_help = \\\n                next(_get_procedure_implementation(name, ds=ds))\n        except StopIteration:\n            # TODO error result\n            raise ValueError(\"Cannot find procedure with name '%s'\", name)\n\n        if not cmd_tmpl:\n            cmd_type, cmd_tmpl = _guess_exec(procedure_file)\n            if cmd_tmpl is None:\n                raise ValueError(\"No idea how to execute procedure %s. \"\n                                 \"Missing 'execute' permissions?\",\n                                 procedure_file)\n\n        if help_proc:\n            res = get_status_dict(\n                    action='procedure_help',\n                    path=procedure_file,\n                    type='file',\n                    logger=lgr,\n                    refds=ds.path if ds else None,\n                    status='ok',\n                    procedure_callfmt=cmd_tmpl,\n                    message=cmd_help)\n            yield res\n            return\n\n        cmd = cmd_tmpl.format(\n            script=procedure_file,\n            ds=ds.path if ds else '',\n            args=u' '.join(u'\"{}\"'.format(a) for a in args) if args else '')\n        lgr.debug('Attempt to run procedure {} as: {}'.format(\n            name,\n            cmd))\n        for r in Run.__call__(\n                cmd=cmd,\n                dataset=ds,\n                explicit=True,\n                inputs=None,\n                outputs=None,\n                # pass through here\n                on_failure='ignore',\n        ):\n            yield r\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:** `run_procedure` Raises TypeError Due to Multiple `path` Arguments

**Description:**

After recent changes, invoking `run_procedure` with certain parameters results in a `TypeError` because multiple `path` arguments are being passed unintentionally.

**Reproduction Steps:**

```python
res = run_procedure('unknown', on_failure='ignore')
```

**Expected Behavior:**

The `run_procedure` function should handle the case where an unknown procedure name is provided gracefully by yielding a result with a status of `"impossible"` without raising any exceptions.

**Actual Behavior:**

A `TypeError` is raised indicating that multiple values for the `path` argument are being passed:

```
TypeError: test_configs() got multiple values for argument 'path'
```

This prevents the function from handling unknown procedures as intended and disrupts the workflow.
--- 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/run_procedure.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.
#
# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
"""Run prepared procedures (DataLad scripts) on a dataset"""

__docformat__ = 'restructuredtext'


import logging

from glob import iglob
from argparse import REMAINDER
import os
import os.path as op
import stat

from datalad import cfg
from datalad.interface.base import Interface
from datalad.interface.utils import eval_results
from datalad.interface.base import build_doc
from datalad.interface.results import get_status_dict

from datalad.distribution.dataset import Dataset
from datalad.distribution.dataset import require_dataset
from datalad.distribution.dataset import EnsureDataset
from datalad.support.constraints import EnsureNone
from datalad.support.param import Parameter
from datalad.distribution.dataset import datasetmethod
from datalad.support.exceptions import InsufficientArgumentsError
from datalad.support.exceptions import NoDatasetArgumentFound

from datalad.utils import assure_list

# bound dataset methods
from datalad.interface.run import Run
from datalad.distribution.subdatasets import Subdatasets

lgr = logging.getLogger('datalad.interface.run_procedures')


def _get_file_match(dir, name='*'):
    targets = (name, ('[!_]*.py'), ('[!_]*.sh'))
    lgr.debug("Looking for procedure '%s' in '%s'", name, dir)
    for target in targets:
        for m in iglob(op.join(dir, target)):
            m_bn = op.basename(m)
            if name == '*':
                report_name = m_bn[:-3] if m_bn.endswith('.py') or \
                                           m_bn.endswith('.sh') \
                                        else m_bn
                yield m, report_name
            elif m_bn == name or m_bn.startswith('{}.'.format(name)):
                yield m, name


def _get_proc_config(name, ds=None):
    """get configuration of named procedure

    Returns
    -------
    tuple
      (call format string, help string) or possibly None for either value,
      if there's nothing configured
    """
    # figure what ConfigManager to ask
    cm = cfg if ds is None else ds.config
    v = cm.get('datalad.procedures.{}.call-format'.format(name), None)
    h = cm.get('datalad.procedures.{}.help'.format(name), None)
    if isinstance(v, tuple):
        # If multiple values are defined (at the same level), ConfigManager
        # returns a tuple. Go with the last one.
        # TODO: Should we issue a warning or fail entirely?
        return v[-1], h
    else:
        return v, h


def _get_procedure_implementation(name='*', ds=None):
    ds = ds if isinstance(ds, Dataset) else Dataset(ds) if ds else None

    # 1. check system and user account for procedure
    for loc in (cfg.obtain('datalad.locations.user-procedures'),
                cfg.obtain('datalad.locations.system-procedures')):
        for dir in assure_list(loc):
            for m, n in _get_file_match(dir, name):
                yield (m,) + _get_proc_config(n)
    # 2. check dataset for procedure
    if ds is not None and ds.is_installed():
        # could be more than one
        dirs = assure_list(
                ds.config.obtain('datalad.locations.dataset-procedures'))
        for dir in dirs:
            # TODO `get` dirs if necessary
            for m, n in _get_file_match(op.join(ds.path, dir), name):
                yield (m,) + _get_proc_config(n, ds=ds)
        # 1.1. check subdatasets recursively
        for subds in ds.subdatasets(return_type='generator',
                                    result_xfm='datasets'):
            for m, f, h in _get_procedure_implementation(name=name, ds=subds):
                yield m, f, h

    # 3. check extensions for procedure
    # delay heavy import until here
    from pkg_resources import iter_entry_points
    from pkg_resources import resource_isdir
    from pkg_resources import resource_filename
    for entry_point in iter_entry_points('datalad.extensions'):
        # use of '/' here is OK wrt to platform compatibility
        if resource_isdir(entry_point.module_name, 'resources/procedures'):
            for m, n in _get_file_match(
                    resource_filename(
                        entry_point.module_name,
                        'resources/procedures'),
                    name):
                yield (m,) + _get_proc_config(n)
    # 4. at last check datalad itself for procedure
    for m, n in _get_file_match(
            resource_filename('datalad', 'resources/procedures'),
            name):
        yield (m,) + _get_proc_config(n)


def _guess_exec(script_file):
    # TODO check for exec permission and rely on interpreter
    if os.stat(script_file).st_mode & stat.S_IEXEC:
        return ('executable', u'"{script}" "{ds}" {args}')
    elif script_file.endswith('.sh'):
        return (u'bash_script', u'bash "{script}" "{ds}" {args}')
    elif script_file.endswith('.py'):
        return (u'python_script', u'python "{script}" "{ds}" {args}')
    else:
        return None, None


@build_doc
class RunProcedure(Interface):
    """Run prepared procedures (DataLad scripts) on a dataset

    *Concept*

    A "procedure" is an algorithm with the purpose to process a dataset in a
    particular way. Procedures can be useful in a wide range of scenarios,
    like adjusting dataset configuration in a uniform fashion, populating
    a dataset with particular content, or automating other routine tasks,
    such as synchronizing dataset content with certain siblings.

    Implementations of some procedures are shipped together with DataLad,
    but additional procedures can be provided by 1) any DataLad extension,
    2) any dataset, 3) a local user, or 4) a local system administrator.
    DataLad will look for procedures in the following locations and order:

    Directories identified by the configuration settings

    - 'datalad.locations.dataset-procedures'
    - 'datalad.locations.user-procedures' (determined by
      appdirs.user_config_dir; defaults to '$HOME/.config/datalad/procedures'
      on GNU/Linux systems)
    - 'datalad.locations.system-procedures' (determined by
      appdirs.site_config_dir; defaults to '/etc/xdg/datalad/procedures' on
      GNU/Linux systems)

    and subsequently in the 'resources/procedures/' directories of any
    installed extension, and, lastly, of the DataLad installation itself.

    Each configuration setting can occur multiple times to indicate multiple
    directories to be searched. If a procedure matching a given name is found
    (filename without a possible extension), the search is aborted and this
    implementation will be executed. This makes it possible for individual
    datasets, users, or machines to override externally provided procedures
    (enabling the implementation of customizable processing "hooks").


    *Procedure implementation*

    A procedure can be any executable. Executables must have the appropriate
    permissions and, in the case of a script, must contain an appropriate
    "shebang" line. If a procedure is not executable, but its filename ends
    with '.py', it is automatically executed by the 'python' interpreter
    (whichever version is available in the present environment). Likewise,
    procedure implementations ending on '.sh' are executed via 'bash'.

    Procedures can implement any argument handling, but must be capable
    of taking at least one positional argument (the absolute path to the
    dataset they shall operate on).


    *Customize other commands with procedures*

    On execution of any commands, DataLad inspects two additional
    configuration settings:

    - 'datalad.<name>.proc-pre'

    - 'datalad.<name>.proc-post'

    where '<name>' is the name of a DataLad command. Using this mechanism
    DataLad can be instructed to run one or more procedures before or
    after the execution of a given command. For example, configuring
    a set of metadata types in any newly created dataset can be achieved
    via:

      % datalad -c 'datalad.create.proc-post=cfg_metadatatypes xmp image' create -d myds

    As procedures run on datasets, it is necessary to explicitly identify
    the target dataset via the -d (--dataset) option.
    """
    _params_ = dict(
        spec=Parameter(
            args=("spec",),
            metavar='NAME [ARGS]',
            nargs=REMAINDER,
            doc="""Name and possibly additional arguments of the
            to-be-executed procedure."""),
        dataset=Parameter(
            args=("-d", "--dataset"),
            metavar="PATH",
            doc="""specify the dataset to run the procedure on.
            An attempt is made to identify the dataset based on the current
            working directory.""",
            constraints=EnsureDataset() | EnsureNone()),
        discover=Parameter(
            args=('--discover',),
            action='store_true',
            doc="""if given, all configured paths are searched for procedures
            and one result record per discovered procedure is yielded, but
            no procedure is executed"""),
        help_proc=Parameter(
            args=('--help-proc',),
            action='store_true',
            doc="""if given, get a help message for procedure NAME from config
            setting datalad.procedures.NAME.help. [CMD: Note, that this 
            parameter needs to be given before NAME, since it would be regarded 
            as part of ARGS otherwise CMD]"""
        )
    )

    @staticmethod
    @datasetmethod(name='run_procedure')
    @eval_results
    def __call__(
            spec=None,
            dataset=None,
            discover=False,
            help_proc=False):
        if not spec and not discover:
            raise InsufficientArgumentsError('requires at least a procedure name')
        if help_proc and not spec:
            raise InsufficientArgumentsError('requires a procedure name')

        try:
            ds = require_dataset(
                dataset, check_installed=False,
                purpose='run a procedure')
        except NoDatasetArgumentFound:
            ds = None

        if discover:
            reported = set()
            for m, cmd_tmpl, cmd_help in _get_procedure_implementation('*', ds=ds):
                if m in reported:
                    continue
                if not cmd_tmpl:
                    cmd_type, cmd_tmpl = _guess_exec(m)
                if cmd_type is None and cmd_tmpl is None:
                    # doesn't seem like a match
                    continue
                res = get_status_dict(
                    action='run_procedure',
                    path=m,
                    type='file',
                    logger=lgr,
                    refds=ds.path if ds else None,
                    status='ok',
                    procedure_type=cmd_type,
                    procedure_callfmt=cmd_tmpl,
                    message=cmd_type)
                reported.add(m)
                yield res
            return

        if not isinstance(spec, (tuple, list)):
            # maybe coming from config
            import shlex
            spec = shlex.split(spec)
        name = spec[0]
        args = spec[1:]

        try:
            # get the first match an run with it
            procedure_file, cmd_tmpl, cmd_help = \
                next(_get_procedure_implementation(name, ds=ds))
        except StopIteration:
            # TODO error result
            raise ValueError("Cannot find procedure with name '%s'", name)

        if not cmd_tmpl:
            cmd_type, cmd_tmpl = _guess_exec(procedure_file)
            if cmd_tmpl is None:
                raise ValueError("No idea how to execute procedure %s. "
                                 "Missing 'execute' permissions?",
                                 procedure_file)

        if help_proc:
            res = get_status_dict(
                    action='procedure_help',
                    path=procedure_file,
                    type='file',
                    logger=lgr,
                    refds=ds.path if ds else None,
                    status='ok',
                    procedure_callfmt=cmd_tmpl,
                    message=cmd_help)
            yield res
            return

        cmd = cmd_tmpl.format(
            script=procedure_file,
            ds=ds.path if ds else '',
            args=u' '.join(u'"{}"'.format(a) for a in args) if args else '')
        lgr.debug('Attempt to run procedure {} as: {}'.format(
            name,
            cmd))
        for r in Run.__call__(
                cmd=cmd,
                dataset=ds,
                explicit=True,
                inputs=None,
                outputs=None,
                # pass through here
                on_failure='ignore',
        ):
            yield r

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