{
    "file_diffs": [
        {
            "old_file_content": "# emacs: -*- mode: python-mode; 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 functioning of the datalad main cmdline utility \"\"\"\n\nimport os\nimport re\nfrom io import StringIO\nfrom unittest.mock import patch\n\nimport datalad\nfrom ..main import (\n    _fix_datalad_ri,\n    main,\n)\nfrom ..helpers import fail_with_short_help\nfrom datalad import __version__\nfrom datalad.cmd import (\n    WitlessRunner as Runner,\n    StdOutErrCapture,\n)\nfrom datalad.ui.utils import (\n    get_console_width,\n    get_terminal_size,\n)\nfrom datalad.api import (\n    create,\n    Dataset,\n)\nfrom datalad.utils import (\n    chpwd,\n    Path,\n)\nfrom datalad.tests.utils import (\n    SkipTest,\n    assert_equal,\n    assert_in,\n    assert_not_in,\n    assert_raises,\n    assert_re_in,\n    eq_,\n    in_,\n    ok_startswith,\n    on_windows,\n    slow,\n    with_tempfile,\n)\nfrom datalad.support.exceptions import CommandError\n\n\ndef run_main(args, exit_code=0, expect_stderr=False):\n    \"\"\"Run main() of the datalad, do basic checks and provide outputs\n\n    Parameters\n    ----------\n    args : list\n        List of string cmdline arguments to pass\n    exit_code : int\n        Expected exit code. Would raise AssertionError if differs\n    expect_stderr : bool or string\n        Whether to expect stderr output. If string -- match\n\n    Returns\n    -------\n    stdout, stderr  strings\n       Output produced\n    \"\"\"\n    with patch('sys.stderr', new_callable=StringIO) as cmerr:\n        with patch('sys.stdout', new_callable=StringIO) as cmout:\n            with assert_raises(SystemExit) as cm:\n                main([\"datalad\"] + list(args))\n            assert_equal(cm.exception.code, exit_code)\n            stdout = cmout.getvalue()\n            stderr = cmerr.getvalue()\n            if expect_stderr is False:\n                assert_equal(stderr, \"\")\n            elif expect_stderr is True:\n                # do nothing -- just return\n                pass\n            else:\n                # must be a string\n                assert_equal(stderr, expect_stderr)\n    return stdout, stderr\n\n\n# TODO: switch to stdout for --version output\ndef test_version():\n    # we just get a version if not asking for a version of some command\n    stdout, stderr = run_main(['--version'], expect_stderr=True)\n    eq_(stdout.rstrip(), \"datalad %s\" % datalad.__version__)\n\n    stdout, stderr = run_main(['clone', '--version'], expect_stderr=True)\n    ok_startswith(stdout, 'datalad %s\\n' % datalad.__version__)\n    # since https://github.com/datalad/datalad/pull/2733 no license in --version\n    assert_not_in(\"Copyright\", stdout)\n    assert_not_in(\"Permission is hereby granted\", stdout)\n\n    try:\n        import datalad_container\n    except ImportError:\n        pass  # not installed, cannot test with extension\n    else:\n        stdout, stderr = run_main(['containers-list', '--version'], expect_stderr=True)\n        eq_(stdout, 'datalad_container %s\\n' % datalad_container.__version__)\n\n\ndef test_help_np():\n    stdout, stderr = run_main(['--help-np'])\n\n    # Let's extract section titles:\n    # enough of bin/datalad and .tox/py27/bin/datalad -- guarantee consistency! ;)\n    ok_startswith(stdout, 'Usage: datalad')\n    # Sections start/end with * if ran under DATALAD_HELP2MAN mode\n    sections = [l[1:-1] for l in filter(re.compile('^\\*.*\\*$').match, stdout.split('\\n'))]\n    # but order is still not guaranteed (dict somewhere)! TODO\n    # see https://travis-ci.org/datalad/datalad/jobs/80519004\n    # thus testing sets\n    for s in {'Commands for dataset operations',\n              'Commands for metadata handling',\n              'Miscellaneous commands',\n              'General information',\n              'Global options',\n              'Plumbing commands',\n              }:\n        assert_in(s, sections)\n\n    if not get_terminal_size()[0] or 0:\n        raise SkipTest(\n            \"Could not determine terminal size, skipping the rest of the test\")\n\n    # none of the lines must be longer than 80 chars\n    # TODO: decide on   create-sibling and possibly\n    # rewrite-urls\n    accepted_width = get_console_width()\n\n    long_lines = [\"%d %s\" % (len(l), l) for l in stdout.split('\\n')\n                  if len(l) > accepted_width and\n                  '{' not in l  # on nd70 summary line is unsplit\n                  ]\n    if long_lines:\n        raise AssertionError(\n            \"Following lines in --help output were longer than %s chars:\\n%s\"\n            % (accepted_width, '\\n'.join(long_lines))\n        )\n\n\ndef test_usage_on_insufficient_args():\n    stdout, stderr = run_main(['install'], exit_code=2, expect_stderr=True)\n    ok_startswith(stderr, 'usage:')\n\n\ndef test_subcmd_usage_on_unknown_args():\n    stdout, stderr = run_main(['get', '--murks'], exit_code=1, expect_stderr=True)\n    in_('get', stdout)\n\n\ndef test_combined_short_option():\n    stdout, stderr = run_main(['-fjson'], exit_code=2, expect_stderr=True)\n    assert_not_in(\"unrecognized argument\", stderr)\n    assert_in(\"too few arguments\", stderr)\n\n\ndef check_incorrect_option(opts, err_str):\n    # The first line used to be:\n    # stdout, stderr = run_main((sys.argv[0],) + opts, expect_stderr=True, exit_code=2)\n    # But: what do we expect to be in sys.argv[0] here?\n    # It depends on how we invoke the test.\n    # - nosetests -s -v datalad/cmdline/tests/test_main.py would result in:\n    #   sys.argv[0}=='nosetests'\n    # - python -m nose -s -v datalad/cmdline/tests/test_main.py would result in:\n    #   sys.argv[0}=='python -m nose'\n    # - python -c \"import nose; nose.main()\" -s -v datalad/cmdline/tests/test_main.py would result in:\n    #   sys.argv[0]=='-c'\n    # This led to failure in case sys.argv[0] contained an option, that was\n    # defined to be a datalad option too, therefore was a 'known_arg' and was\n    # checked to meet its constraints.\n    # But sys.argv[0] actually isn't used by main at all. It simply doesn't\n    # matter what's in there. The only thing important to pass here is `opts`.\n    stdout, stderr = run_main(opts, expect_stderr=True, exit_code=2)\n    out = stdout + stderr\n    assert_in(\"usage: \", out)\n    assert_re_in(err_str, out, match=False)\n\n\ndef test_incorrect_options():\n    # apparently a bit different if following a good one so let's do both\n    err_invalid = \"error: (invalid|too few arguments|unrecognized argument)\"\n    yield check_incorrect_option, ('--buga',), err_invalid\n    yield check_incorrect_option, ('--dbg', '--buga'), err_invalid\n\n    err_insufficient = err_invalid  # \"specify\"\n    yield check_incorrect_option, ('--dbg',), err_insufficient\n    yield check_incorrect_option, tuple(), err_insufficient\n\n\ndef test_script_shims():\n    runner = Runner()\n    for script in [\n        'datalad',\n        'git-annex-remote-datalad-archives',\n        'git-annex-remote-datalad']:\n        if not on_windows:\n            # those must be available for execution, and should not contain\n            which = runner.run(['which', script], protocol=StdOutErrCapture)['stdout']\n            # test if there is no easy install shim in there\n            with open(which.rstrip()) as f:\n                content = f.read()\n        else:\n            from distutils.spawn import find_executable\n            content = find_executable(script)\n\n        # and let's check that it is our script\n        out = runner.run([script, '--version'], protocol=StdOutErrCapture)\n        version = out['stdout'].rstrip()\n        mod, version = version.split(' ', 1)\n        assert_equal(mod, 'datalad')\n        # we can get git and non git .dev version... so for now\n        # relax\n        get_numeric_portion = lambda v: [x for x in re.split('[+.]', v) if x.isdigit()]\n        # extract numeric portion\n        assert get_numeric_portion(version), f\"Got no numeric portion from {version}\"\n        assert_equal(get_numeric_portion(__version__),\n                     get_numeric_portion(version))\n\n\n@slow  # 11.2591s\n@with_tempfile(mkdir=True)\ndef test_cfg_override(path):\n    with chpwd(path):\n        cmd = ['datalad', 'wtf', '-s', 'some']\n        # control\n        out = Runner().run(cmd, protocol=StdOutErrCapture)['stdout']\n        assert_not_in('datalad.dummy: this', out)\n        # ensure that this is not a dataset's cfg manager\n        assert_not_in('datalad.dataset.id', out)\n        # env var\n        out = Runner(env=dict(os.environ, DATALAD_DUMMY='this')).run(\n            cmd, protocol=StdOutErrCapture)['stdout']\n        assert_in('datalad.dummy: this', out)\n        # cmdline arg\n        out = Runner().run([cmd[0], '-c', 'datalad.dummy=this'] + cmd[1:],\n                           protocol=StdOutErrCapture)['stdout']\n        assert_in('datalad.dummy: this', out)\n\n        # now create a dataset in the path. the wtf plugin will switch to\n        # using the dataset's config manager, which must inherit the overrides\n        create(dataset=path)\n        # control\n        out = Runner().run(cmd, protocol=StdOutErrCapture)['stdout']\n        assert_not_in('datalad.dummy: this', out)\n        # ensure that this is a dataset's cfg manager\n        assert_in('datalad.dataset.id', out)\n        # env var\n        out = Runner(env=dict(os.environ, DATALAD_DUMMY='this')).run(\n            cmd, protocol=StdOutErrCapture)['stdout']\n        assert_in('datalad.dummy: this', out)\n        # cmdline arg\n        out = Runner().run([cmd[0], '-c', 'datalad.dummy=this'] + cmd[1:],\n                           protocol=StdOutErrCapture)['stdout']\n        assert_in('datalad.dummy: this', out)\n\n\ndef test_incorrect_cfg_override():\n    run_main(['-c', 'some', 'wtf'], exit_code=3)\n    run_main(['-c', 'some=', 'wtf'], exit_code=3)\n    run_main(['-c', 'some.var', 'wtf'], exit_code=3)\n    run_main(['-c', 'some.var=', 'wtf'], exit_code=3)\n\n\ndef test_fail_with_short_help():\n    out = StringIO()\n    with assert_raises(SystemExit) as cme:\n        fail_with_short_help(exit_code=3, out=out)\n    assert_equal(cme.exception.code, 3)\n    assert_equal(out.getvalue(), \"\")\n\n    out = StringIO()\n    with assert_raises(SystemExit) as cme:\n        fail_with_short_help(msg=\"Failed badly\", out=out)\n    assert_equal(cme.exception.code, 1)\n    assert_equal(out.getvalue(), \"error: Failed badly\\n\")\n\n    # Suggestions, hint, etc\n    out = StringIO()\n    with assert_raises(SystemExit) as cme:\n        fail_with_short_help(\n            msg=\"Failed badly\",\n            known=[\"mother\", \"mutter\", \"father\", \"son\"],\n            provided=\"muther\",\n            hint=\"You can become one\",\n            exit_code=0,  # nobody forbids\n            what=\"parent\",\n            out=out)\n    assert_equal(cme.exception.code, 0)\n    assert_equal(out.getvalue(),\n                 \"error: Failed badly\\n\"\n                 \"datalad: Unknown parent 'muther'.  See 'datalad --help'.\\n\\n\"\n                 \"Did you mean any of these?\\n\"\n                 \"        mutter\\n\"\n                 \"        mother\\n\"\n                 \"        father\\n\"\n                 \"Hint: You can become one\\n\")\n\ndef test_fix_datalad_ri():\n    assert_equal(_fix_datalad_ri('/'), '/')\n    assert_equal(_fix_datalad_ri('/a/b'), '/a/b')\n    assert_equal(_fix_datalad_ri('//'), '///')\n    assert_equal(_fix_datalad_ri('///'), '///')\n    assert_equal(_fix_datalad_ri('//a'), '///a')\n    assert_equal(_fix_datalad_ri('///a'), '///a')\n    assert_equal(_fix_datalad_ri('//a/b'), '///a/b')\n    assert_equal(_fix_datalad_ri('///a/b'), '///a/b')\n\n\ndef test_fail_with_short_help():\n    out = StringIO()\n    with assert_raises(SystemExit) as cme:\n        fail_with_short_help(exit_code=3, out=out)\n    assert_equal(cme.exception.code, 3)\n    assert_equal(out.getvalue(), \"\")\n\n    out = StringIO()\n    with assert_raises(SystemExit) as cme:\n        fail_with_short_help(msg=\"Failed badly\", out=out)\n    assert_equal(cme.exception.code, 1)\n    assert_equal(out.getvalue(), \"error: Failed badly\\n\")\n\n    # Suggestions, hint, etc\n    out = StringIO()\n    with assert_raises(SystemExit) as cme:\n        fail_with_short_help(\n            msg=\"Failed badly\",\n            known=[\"mother\", \"mutter\", \"father\", \"son\"],\n            provided=\"muther\",\n            hint=\"You can become one\",\n            exit_code=0,  # nobody forbids\n            what=\"parent\",\n            out=out)\n    assert_equal(cme.exception.code, 0)\n    assert_equal(out.getvalue(),\n                 \"error: Failed badly\\n\"\n                 \"datalad: Unknown parent 'muther'.  See 'datalad --help'.\\n\\n\"\n                 \"Did you mean any of these?\\n\"\n                 \"        mutter\\n\"\n                 \"        mother\\n\"\n                 \"        father\\n\"\n                 \"Hint: You can become one\\n\")\n\n\ndef test_fix_datalad_ri():\n    assert_equal(_fix_datalad_ri('/'), '/')\n    assert_equal(_fix_datalad_ri('/a/b'), '/a/b')\n    assert_equal(_fix_datalad_ri('//'), '///')\n    assert_equal(_fix_datalad_ri('///'), '///')\n    assert_equal(_fix_datalad_ri('//a'), '///a')\n    assert_equal(_fix_datalad_ri('///a'), '///a')\n    assert_equal(_fix_datalad_ri('//a/b'), '///a/b')\n    assert_equal(_fix_datalad_ri('///a/b'), '///a/b')\n\n\n@with_tempfile\n@with_tempfile(mkdir=True)\ndef test_commanderror_jsonmsgs(src, exp):\n    ds = Dataset(src).create()\n    (ds.pathobj / '123').write_text('123')\n    ds.save()\n    ds.repo.call_annex([\n        'initremote', 'expdir', 'type=directory',\n        'directory={}'.format(exp),\n        'encryption=none',\n        'exporttree=yes'\n    ])\n    #ds.repo.call_annex(['export', '--to=expdir', 'HEAD'])\n    # this must fail, because `push` cannot handle an export.\n    # when https://github.com/datalad/datalad/issues/3127 is implemented\n    # this test must be adjusted\n    with assert_raises(CommandError) as cme:\n        Runner(cwd=ds.path).run(\n            ['datalad', 'push', '--to', 'expdir'],\n            protocol=StdOutErrCapture)\n    if ds.repo.git_annex_version >= \"8.20200309\":\n        in_('use `git-annex export`', cme.exception.stderr)\n",
            "new_file_content": "# emacs: -*- mode: python-mode; 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 functioning of the datalad main cmdline utility \"\"\"\n\nimport os\nimport re\nfrom io import StringIO\nfrom unittest.mock import patch\n\nimport datalad\nfrom ..main import (\n    _fix_datalad_ri,\n    main,\n)\nfrom ..helpers import fail_with_short_help\nfrom datalad import __version__\nfrom datalad.cmd import (\n    WitlessRunner as Runner,\n    StdOutErrCapture,\n)\nfrom datalad.ui.utils import (\n    get_console_width,\n    get_terminal_size,\n)\nfrom datalad.api import (\n    create,\n    Dataset,\n)\nfrom datalad.utils import (\n    chpwd,\n    Path,\n)\nfrom datalad.tests.utils import (\n    SkipTest,\n    assert_equal,\n    assert_in,\n    assert_not_in,\n    assert_raises,\n    assert_re_in,\n    eq_,\n    in_,\n    ok_startswith,\n    on_windows,\n    slow,\n    with_tempfile,\n)\nfrom datalad.support.exceptions import CommandError\n\n\ndef run_main(args, exit_code=0, expect_stderr=False):\n    \"\"\"Run main() of the datalad, do basic checks and provide outputs\n\n    Parameters\n    ----------\n    args : list\n        List of string cmdline arguments to pass\n    exit_code : int\n        Expected exit code. Would raise AssertionError if differs\n    expect_stderr : bool or string\n        Whether to expect stderr output. If string -- match\n\n    Returns\n    -------\n    stdout, stderr  strings\n       Output produced\n    \"\"\"\n    with patch('sys.stderr', new_callable=StringIO) as cmerr:\n        with patch('sys.stdout', new_callable=StringIO) as cmout:\n            with assert_raises(SystemExit) as cm:\n                main([\"datalad\"] + list(args))\n            assert_equal(cm.exception.code, exit_code)\n            stdout = cmout.getvalue()\n            stderr = cmerr.getvalue()\n            if expect_stderr is False:\n                assert_equal(stderr, \"\")\n            elif expect_stderr is True:\n                # do nothing -- just return\n                pass\n            else:\n                # must be a string\n                assert_equal(stderr, expect_stderr)\n    return stdout, stderr\n\n\n# TODO: switch to stdout for --version output\ndef test_version():\n    # we just get a version if not asking for a version of some command\n    stdout, stderr = run_main(['--version'], expect_stderr=True)\n    eq_(stdout.rstrip(), \"datalad %s\" % datalad.__version__)\n\n    stdout, stderr = run_main(['clone', '--version'], expect_stderr=True)\n    ok_startswith(stdout, 'datalad %s\\n' % datalad.__version__)\n    # since https://github.com/datalad/datalad/pull/2733 no license in --version\n    assert_not_in(\"Copyright\", stdout)\n    assert_not_in(\"Permission is hereby granted\", stdout)\n\n    try:\n        import datalad_container\n    except ImportError:\n        pass  # not installed, cannot test with extension\n    else:\n        stdout, stderr = run_main(['containers-list', '--version'], expect_stderr=True)\n        eq_(stdout, 'datalad_container %s\\n' % datalad_container.__version__)\n\n\ndef test_help_np():\n    stdout, stderr = run_main(['--help-np'])\n\n    # Let's extract section titles:\n    # enough of bin/datalad and .tox/py27/bin/datalad -- guarantee consistency! ;)\n    ok_startswith(stdout, 'Usage: datalad')\n    # Sections start/end with * if ran under DATALAD_HELP2MAN mode\n    sections = [l[1:-1] for l in filter(re.compile('^\\*.*\\*$').match, stdout.split('\\n'))]\n    # but order is still not guaranteed (dict somewhere)! TODO\n    # see https://travis-ci.org/datalad/datalad/jobs/80519004\n    # thus testing sets\n    for s in {'Commands for dataset operations',\n              'Commands for metadata handling',\n              'Miscellaneous commands',\n              'General information',\n              'Global options',\n              'Plumbing commands',\n              }:\n        assert_in(s, sections)\n\n    if not get_terminal_size()[0] or 0:\n        raise SkipTest(\n            \"Could not determine terminal size, skipping the rest of the test\")\n\n    # none of the lines must be longer than 80 chars\n    # TODO: decide on   create-sibling and possibly\n    # rewrite-urls\n    accepted_width = get_console_width()\n\n    long_lines = [\"%d %s\" % (len(l), l) for l in stdout.split('\\n')\n                  if len(l) > accepted_width and\n                  '{' not in l  # on nd70 summary line is unsplit\n                  ]\n    if long_lines:\n        raise AssertionError(\n            \"Following lines in --help output were longer than %s chars:\\n%s\"\n            % (accepted_width, '\\n'.join(long_lines))\n        )\n\n\ndef test_usage_on_insufficient_args():\n    stdout, stderr = run_main(['install'], exit_code=2, expect_stderr=True)\n    ok_startswith(stderr, 'usage:')\n\n\ndef test_subcmd_usage_on_unknown_args():\n    stdout, stderr = run_main(['get', '--murks'], exit_code=1, expect_stderr=True)\n    in_('get', stdout)\n\n\ndef test_combined_short_option():\n    stdout, stderr = run_main(['-fjson'], exit_code=2, expect_stderr=True)\n    assert_not_in(\"unrecognized argument\", stderr)\n    assert_in(\"too few arguments\", stderr)\n\n\ndef check_incorrect_option(opts, err_str):\n    # The first line used to be:\n    # stdout, stderr = run_main((sys.argv[0],) + opts, expect_stderr=True, exit_code=2)\n    # But: what do we expect to be in sys.argv[0] here?\n    # It depends on how we invoke the test.\n    # - nosetests -s -v datalad/cmdline/tests/test_main.py would result in:\n    #   sys.argv[0}=='nosetests'\n    # - python -m nose -s -v datalad/cmdline/tests/test_main.py would result in:\n    #   sys.argv[0}=='python -m nose'\n    # - python -c \"import nose; nose.main()\" -s -v datalad/cmdline/tests/test_main.py would result in:\n    #   sys.argv[0]=='-c'\n    # This led to failure in case sys.argv[0] contained an option, that was\n    # defined to be a datalad option too, therefore was a 'known_arg' and was\n    # checked to meet its constraints.\n    # But sys.argv[0] actually isn't used by main at all. It simply doesn't\n    # matter what's in there. The only thing important to pass here is `opts`.\n    stdout, stderr = run_main(opts, expect_stderr=True, exit_code=2)\n    out = stdout + stderr\n    assert_in(\"usage: \", out)\n    assert_re_in(err_str, out, match=False)\n\n\ndef test_incorrect_options():\n    # apparently a bit different if following a good one so let's do both\n    err_invalid = \"error: (invalid|too few arguments|unrecognized argument)\"\n    yield check_incorrect_option, ('--buga',), err_invalid\n    yield check_incorrect_option, ('--dbg', '--buga'), err_invalid\n\n    err_insufficient = err_invalid  # \"specify\"\n    yield check_incorrect_option, ('--dbg',), err_insufficient\n    yield check_incorrect_option, tuple(), err_insufficient\n\n\ndef test_script_shims():\n    runner = Runner()\n    for script in [\n        'datalad',\n        'git-annex-remote-datalad-archives',\n        'git-annex-remote-datalad']:\n        if not on_windows:\n            # those must be available for execution, and should not contain\n            which = runner.run(['which', script], protocol=StdOutErrCapture)['stdout']\n            # test if there is no easy install shim in there\n            with open(which.rstrip()) as f:\n                content = f.read()\n        else:\n            from distutils.spawn import find_executable\n            content = find_executable(script)\n\n        # and let's check that it is our script\n        out = runner.run([script, '--version'], protocol=StdOutErrCapture)\n        version = out['stdout'].rstrip()\n        mod, version = version.split(' ', 1)\n        assert_equal(mod, 'datalad')\n        # we can get git and non git .dev version... so for now\n        # relax\n        get_numeric_portion = lambda v: [x for x in re.split('[+.]', v) if x.isdigit()]\n        # extract numeric portion\n        assert get_numeric_portion(version), f\"Got no numeric portion from {version}\"\n        assert_equal(get_numeric_portion(__version__),\n                     get_numeric_portion(version))\n\n\n@slow  # 11.2591s\n@with_tempfile(mkdir=True)\ndef test_cfg_override(path):\n    with chpwd(path):\n        cmd = ['datalad', 'wtf', '-s', 'some']\n        # control\n        out = Runner().run(cmd, protocol=StdOutErrCapture)['stdout']\n        assert_not_in('datalad.dummy: this', out)\n        # ensure that this is not a dataset's cfg manager\n        assert_not_in('datalad.dataset.id', out)\n        # env var\n        out = Runner(env=dict(os.environ, DATALAD_DUMMY='this')).run(\n            cmd, protocol=StdOutErrCapture)['stdout']\n        assert_in('datalad.dummy: this', out)\n        # cmdline arg\n        out = Runner().run([cmd[0], '-c', 'datalad.dummy=this'] + cmd[1:],\n                           protocol=StdOutErrCapture)['stdout']\n        assert_in('datalad.dummy: this', out)\n\n        # now create a dataset in the path. the wtf plugin will switch to\n        # using the dataset's config manager, which must inherit the overrides\n        create(dataset=path)\n        # control\n        out = Runner().run(cmd, protocol=StdOutErrCapture)['stdout']\n        assert_not_in('datalad.dummy: this', out)\n        # ensure that this is a dataset's cfg manager\n        assert_in('datalad.dataset.id', out)\n        # env var\n        out = Runner(env=dict(os.environ, DATALAD_DUMMY='this')).run(\n            cmd, protocol=StdOutErrCapture)['stdout']\n        assert_in('datalad.dummy: this', out)\n        # cmdline arg\n        out = Runner().run([cmd[0], '-c', 'datalad.dummy=this'] + cmd[1:],\n                           protocol=StdOutErrCapture)['stdout']\n        assert_in('datalad.dummy: this', out)\n\n\ndef test_incorrect_cfg_override():\n    run_main(['-c', 'some', 'wtf'], exit_code=3)\n    run_main(['-c', 'some=', 'wtf'], exit_code=3)\n    run_main(['-c', 'some.var', 'wtf'], exit_code=3)\n    run_main(['-c', 'some.var=', 'wtf'], exit_code=3)\n\n\ndef test_fail_with_short_help():\n    out = StringIO()\n    with assert_raises(SystemExit) as cme:\n        fail_with_short_help(exit_code=3, out=out)\n    assert_equal(cme.exception.code, 3)\n    assert_equal(out.getvalue(), \"\")\n\n    out = StringIO()\n    with assert_raises(SystemExit) as cme:\n        fail_with_short_help(msg=\"Failed badly\", out=out)\n    assert_equal(cme.exception.code, 1)\n    assert_equal(out.getvalue(), \"error: Failed badly\\n\")\n\n    # Suggestions, hint, etc\n    out = StringIO()\n    with assert_raises(SystemExit) as cme:\n        fail_with_short_help(\n            msg=\"Failed badly\",\n            known=[\"mother\", \"mutter\", \"father\", \"son\"],\n            provided=\"muther\",\n            hint=\"You can become one\",\n            exit_code=0,  # nobody forbids\n            what=\"parent\",\n            out=out)\n    assert_equal(cme.exception.code, 0)\n    assert_equal(out.getvalue(),\n                 \"error: Failed badly\\n\"\n                 \"datalad: Unknown parent 'muther'.  See 'datalad --help'.\\n\\n\"\n                 \"Did you mean any of these?\\n\"\n                 \"        mutter\\n\"\n                 \"        mother\\n\"\n                 \"        father\\n\"\n                 \"Hint: You can become one\\n\")\n\ndef test_fix_datalad_ri():\n    assert_equal(_fix_datalad_ri('/'), '/')\n    assert_equal(_fix_datalad_ri('/a/b'), '/a/b')\n    assert_equal(_fix_datalad_ri('//'), '///')\n    assert_equal(_fix_datalad_ri('///'), '///')\n    assert_equal(_fix_datalad_ri('//a'), '///a')\n    assert_equal(_fix_datalad_ri('///a'), '///a')\n    assert_equal(_fix_datalad_ri('//a/b'), '///a/b')\n    assert_equal(_fix_datalad_ri('///a/b'), '///a/b')\n\n\ndef test_fail_with_short_help():\n    out = StringIO()\n    with assert_raises(SystemExit) as cme:\n        fail_with_short_help(exit_code=3, out=out)\n    assert_equal(cme.exception.code, 3)\n    assert_equal(out.getvalue(), \"\")\n\n    out = StringIO()\n    with assert_raises(SystemExit) as cme:\n        fail_with_short_help(msg=\"Failed badly\", out=out)\n    assert_equal(cme.exception.code, 1)\n    assert_equal(out.getvalue(), \"error: Failed badly\\n\")\n\n    # Suggestions, hint, etc\n    out = StringIO()\n    with assert_raises(SystemExit) as cme:\n        fail_with_short_help(\n            msg=\"Failed badly\",\n            known=[\"mother\", \"mutter\", \"father\", \"son\"],\n            provided=\"muther\",\n            hint=\"You can become one\",\n            exit_code=0,  # nobody forbids\n            what=\"parent\",\n            out=out)\n    assert_equal(cme.exception.code, 0)\n    assert_equal(out.getvalue(),\n                 \"error: Failed badly\\n\"\n                 \"datalad: Unknown parent 'muther'.  See 'datalad --help'.\\n\\n\"\n                 \"Did you mean any of these?\\n\"\n                 \"        mutter\\n\"\n                 \"        mother\\n\"\n                 \"        father\\n\"\n                 \"Hint: You can become one\\n\")\n\n\ndef test_fix_datalad_ri():\n    assert_equal(_fix_datalad_ri('/'), '/')\n    assert_equal(_fix_datalad_ri('/a/b'), '/a/b')\n    assert_equal(_fix_datalad_ri('//'), '///')\n    assert_equal(_fix_datalad_ri('///'), '///')\n    assert_equal(_fix_datalad_ri('//a'), '///a')\n    assert_equal(_fix_datalad_ri('///a'), '///a')\n    assert_equal(_fix_datalad_ri('//a/b'), '///a/b')\n    assert_equal(_fix_datalad_ri('///a/b'), '///a/b')\n\n\n@with_tempfile\n@with_tempfile(mkdir=True)\ndef test_commanderror_jsonmsgs(src, exp):\n    ds = Dataset(src).create()\n    (ds.pathobj / '123').write_text('123')\n    ds.save()\n    ds.repo.call_annex([\n        'initremote', 'expdir', 'type=directory',\n        'directory={}'.format(exp),\n        'encryption=none',\n        'exporttree=yes'\n    ])\n    #ds.repo.call_annex(['export', '--to=expdir', 'HEAD'])\n    # this must fail, because `push` cannot handle an export.\n    # when https://github.com/datalad/datalad/issues/3127 is implemented\n    # this test must be adjusted\n    with assert_raises(CommandError) as cme:\n        Runner(cwd=ds.path).run(\n            ['datalad', 'push', '--to', 'expdir'],\n            protocol=StdOutErrCapture)\n    if ds.repo.git_annex_version >= \"8.20201129\":\n        in_('use `git-annex export`', cme.exception.stderr)\n",
            "header": {
                "file": {
                    "path": "datalad/cmdline/tests/test_main.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "4a1878272",
                "new_commit_hash": "2d5bc6f5c",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/datalad/cmdline/tests/test_main.py"
            },
            "plus_file": {
                "path": "b/datalad/cmdline/tests/test_main.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 383,
                            "length": 5
                        },
                        "new_range": {
                            "start": 383,
                            "length": 5
                        },
                        "section": "def test_commanderror_jsonmsgs(src, exp):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        Runner(cwd=ds.path).run(",
                                "type": "context"
                            },
                            {
                                "content": "            ['datalad', 'push', '--to', 'expdir'],",
                                "type": "context"
                            },
                            {
                                "content": "            protocol=StdOutErrCapture)",
                                "type": "context"
                            },
                            {
                                "content": "    if ds.repo.git_annex_version >= \"8.20200309\":",
                                "type": "deleted"
                            },
                            {
                                "content": "    if ds.repo.git_annex_version >= \"8.20201129\":",
                                "type": "added"
                            },
                            {
                                "content": "        in_('use `git-annex export`', cme.exception.stderr)",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "datalad/cmdline/tests/test_main.py",
                            "type": "function",
                            "name": "test_commanderror_jsonmsgs",
                            "content": "def test_commanderror_jsonmsgs(src, exp):\n    ds = Dataset(src).create()\n    (ds.pathobj / '123').write_text('123')\n    ds.save()\n    ds.repo.call_annex([\n        'initremote', 'expdir', 'type=directory',\n        'directory={}'.format(exp),\n        'encryption=none',\n        'exporttree=yes'\n    ])\n    #ds.repo.call_annex(['export', '--to=expdir', 'HEAD'])\n    # this must fail, because `push` cannot handle an export.\n    # when https://github.com/datalad/datalad/issues/3127 is implemented\n    # this test must be adjusted\n    with assert_raises(CommandError) as cme:\n        Runner(cwd=ds.path).run(\n            ['datalad', 'push', '--to', 'expdir'],\n            protocol=StdOutErrCapture)\n    if ds.repo.git_annex_version >= \"8.20200309\":\n        in_('use `git-annex export`', cme.exception.stderr)",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 368,
                            "end_lineno": 387,
                            "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\"\"\"Helper for RIA stores\n\n\"\"\"\n\nimport logging\nfrom pathlib import Path\n\n\nlgr = logging.getLogger('datalad.customremotes.ria_utils')\n\n\nclass UnknownLayoutVersion(Exception):\n    pass\n\n\n# TODO: Make versions a tuple of (label, description)?\n# Object tree versions we introduced so far. This is about the layout within a\n# dataset in a RIA store\nknown_versions_objt = ['1', '2']\n# Dataset tree versions we introduced so far. This is about the layout of\n# datasets in a RIA store\nknown_versions_dst = ['1']\n\n\n# TODO: This is wrong and should consider both versions (store+dataset)\ndef get_layout_locations(version, base_path, dsid):\n    \"\"\"Return dataset-related path in a RIA store\n\n    Parameters\n    ----------\n    version : int\n      Layout version of the store.\n    base_path : Path\n      Base path of the store.\n    dsid : str\n      Dataset ID\n\n    Returns\n    -------\n    Path, Path, Path\n      The location of the bare dataset repository in the store,\n      the directory with archive files for the dataset, and the\n      annex object directory are return in that order.\n    \"\"\"\n    if version == 1:\n        dsgit_dir = base_path / dsid[:3] / dsid[3:]\n        archive_dir = dsgit_dir / 'archives'\n        dsobj_dir = dsgit_dir / 'annex' / 'objects'\n        return dsgit_dir, archive_dir, dsobj_dir\n    else:\n        raise ValueError(\"Unknown layout version: {}. Supported: {}\"\n                         \"\".format(version, known_versions_dst))\n\n\ndef verify_ria_url(url, cfg):\n    \"\"\"Verify and decode ria url\n\n    Expects a ria-URL pointing to a RIA store, applies rewrites and tries to\n    decode potential host and base path for the store from it. Additionally\n    raises if `url` is considered invalid.\n\n    ria+ssh://somehost:/path/to/store\n    ria+file:///path/to/store\n\n    Parameters\n    ----------\n    url : str\n      URL to verify an decode.\n    cfg : dict-like\n      Configuration settings for rewrite_url()\n\n    Raises\n    ------\n    ValueError\n\n    Returns\n    -------\n    tuple\n      (host, base-path, rewritten url)\n    \"\"\"\n    from datalad.config import rewrite_url\n    from datalad.support.network import URL\n\n    if not url:\n        raise ValueError(\"Got no URL\")\n\n    url = rewrite_url(cfg, url)\n    url_ri = URL(url)\n    if not url_ri.scheme.startswith('ria+'):\n        raise ValueError(\"Missing ria+ prefix in final URL: %s\" % url)\n    if url_ri.fragment:\n        raise ValueError(\n            \"Unexpected fragment in RIA-store URL: %s\" % url_ri.fragment)\n    protocol = url_ri.scheme[4:]\n    if protocol not in ['ssh', 'file', 'http', 'https']:\n        raise ValueError(\"Unsupported protocol: %s. \"\n                         \"Supported: ssh, file, http(s)\" %\n                         protocol)\n\n    return url_ri.hostname if protocol != 'file' else None, \\\n        url_ri.path if url_ri.path else '/', \\\n        url\n\n\ndef _ensure_version(io, base_path, version):\n    \"\"\"Check a store or dataset version and make sure it is declared\n\n    Parameters\n    ----------\n    io: SSHRemoteIO or LocalIO\n    base_path: Path\n      root path of a store or dataset\n    version: str\n      target layout version of the store (dataset tree)\n    \"\"\"\n    version_file = base_path / 'ria-layout-version'\n    if io.exists(version_file):\n        existing_version = io.read_file(version_file).split('|')[0].strip()\n        if existing_version != version.split('|')[0]:\n            # We have an already existing location with a conflicting version on\n            # record.\n            # Note, that a config flag after pipe symbol is fine.\n            raise ValueError(\"Conflicting version found at target: {}\"\n                             .format(existing_version))\n        else:\n            # already exists, recorded version fits - nothing to do\n            return\n    # Note, that the following does create the base-path dir as well, since\n    # mkdir has parents=True:\n    io.mkdir(base_path)\n    io.write_file(version_file, version)\n\n\ndef create_store(io, base_path, version):\n    \"\"\"Helper to create a RIA store\n\n    Note, that this is meant as an internal helper and part of intermediate\n    RF'ing. Ultimately should lead to dedicated command or option for\n    create-sibling-ria.\n\n    Parameters\n    ----------\n    io: SSHRemoteIO or LocalIO\n      Respective execution instance.\n      Note: To be replaced by proper command abstraction\n    base_path: Path\n      root path of the store\n    version: str\n      layout version of the store (dataset tree)\n    \"\"\"\n\n    # At store level the only version we know as of now is 1.\n    if version not in known_versions_dst:\n        raise UnknownLayoutVersion(\"RIA store layout version unknown: {}.\"\n                                   \"Supported versions: {}\"\n                                   .format(version, known_versions_dst))\n    _ensure_version(io, base_path, version)\n    error_logs = base_path / 'error_logs'\n    io.mkdir(error_logs)\n\n\ndef create_ds_in_store(io, base_path, dsid, obj_version, store_version, alias=None):\n    \"\"\"Helper to create a dataset in a RIA store\n\n    Note, that this is meant as an internal helper and part of intermediate\n    RF'ing. Ultimately should lead to a version option for create-sibling-ria\n    in conjunction with a store creation command/option.\n\n    Parameters\n    ----------\n    io: SSHRemoteIO or LocalIO\n      Respective execution instance.\n      Note: To be replaced by proper command abstraction\n    base_path: Path\n      root path of the store\n    dsid: str\n      dataset id\n    store_version: str\n      layout version of the store (dataset tree)\n    obj_version: str\n      layout version of the dataset itself (object tree)\n    alias: str, optional\n      alias for the dataset in the store\n    \"\"\"\n\n    # TODO: Note for RF'ing, that this is about setting up a valid target\n    #       for the special remote not a replacement for create-sibling-ria.\n    #       There's currently no git (bare) repo created.\n\n    try:\n        # TODO: This is currently store layout version!\n        #       Too entangled by current get_layout_locations.\n        dsgit_dir, archive_dir, dsobj_dir = \\\n            get_layout_locations(int(store_version), base_path, dsid)\n    except ValueError as e:\n        raise UnknownLayoutVersion(str(e))\n\n    if obj_version not in known_versions_objt:\n        raise UnknownLayoutVersion(\"Dataset layout version unknown: {}. \"\n                                   \"Supported: {}\"\n                                   .format(obj_version, known_versions_objt))\n\n    _ensure_version(io, dsgit_dir, obj_version)\n\n    io.mkdir(archive_dir)\n    io.mkdir(dsobj_dir)\n    if alias:\n        alias_dir = base_path / \"alias\"\n        io.mkdir(alias_dir)\n        try:\n            # go for a relative path to keep the alias links valid\n            # when moving a store\n            io.symlink(\n                Path('..') / dsgit_dir.relative_to(base_path),\n                alias_dir / alias)\n        except FileExistsError:\n            lgr.warning(\"Alias %r already exists in the RIA store, not adding an \"\n                        \"alias.\", alias)\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\"\"\"Helper for RIA stores\n\n\"\"\"\n\nimport logging\nfrom pathlib import Path\n\n\nlgr = logging.getLogger('datalad.customremotes.ria_utils')\n\n\nclass UnknownLayoutVersion(Exception):\n    pass\n\n\n# TODO: Make versions a tuple of (label, description)?\n# Object tree versions we introduced so far. This is about the layout within a\n# dataset in a RIA store\nknown_versions_objt = ['1', '2']\n# Dataset tree versions we introduced so far. This is about the layout of\n# datasets in a RIA store\nknown_versions_dst = ['1']\n\n\n# TODO: This is wrong and should consider both versions (store+dataset)\ndef get_layout_locations(version, base_path, dsid):\n    \"\"\"Return dataset-related path in a RIA store\n\n    Parameters\n    ----------\n    version : int\n      Layout version of the store.\n    base_path : Path\n      Base path of the store.\n    dsid : str\n      Dataset ID\n\n    Returns\n    -------\n    Path, Path, Path\n      The location of the bare dataset repository in the store,\n      the directory with archive files for the dataset, and the\n      annex object directory are return in that order.\n    \"\"\"\n    if version == 1:\n        dsgit_dir = base_path / dsid[:3] / dsid[3:]\n        archive_dir = dsgit_dir / 'archives'\n        dsobj_dir = dsgit_dir / 'annex' / 'objects'\n        return dsgit_dir, archive_dir, dsobj_dir\n    else:\n        raise ValueError(\"Unknown layout version: {}. Supported: {}\"\n                         \"\".format(version, known_versions_dst))\n\n\ndef verify_ria_url(url, cfg):\n    \"\"\"Verify and decode ria url\n\n    Expects a ria-URL pointing to a RIA store, applies rewrites and tries to\n    decode potential host and base path for the store from it. Additionally\n    raises if `url` is considered invalid.\n\n    ria+ssh://somehost:/path/to/store\n    ria+file:///path/to/store\n\n    Parameters\n    ----------\n    url : str\n      URL to verify an decode.\n    cfg : dict-like\n      Configuration settings for rewrite_url()\n\n    Raises\n    ------\n    ValueError\n\n    Returns\n    -------\n    tuple\n      (host, base-path, rewritten url)\n      `host` is not just a hostname, but is a stub URL that may also contain\n      username, password, and port, if specified in a given URL.\n    \"\"\"\n    from datalad.config import rewrite_url\n    from datalad.support.network import URL\n\n    if not url:\n        raise ValueError(\"Got no URL\")\n\n    url = rewrite_url(cfg, url)\n    url_ri = URL(url)\n    if not url_ri.scheme.startswith('ria+'):\n        raise ValueError(\"Missing ria+ prefix in final URL: %s\" % url)\n    if url_ri.fragment:\n        raise ValueError(\n            \"Unexpected fragment in RIA-store URL: %s\" % url_ri.fragment)\n    protocol = url_ri.scheme[4:]\n    if protocol not in ['ssh', 'file', 'http', 'https']:\n        raise ValueError(\"Unsupported protocol: %s. \"\n                         \"Supported: ssh, file, http(s)\" %\n                         protocol)\n\n    host = '{proto}://{user}{pdlm}{passwd}{udlm}{host}{portdlm}{port}'.format(\n        proto=protocol,\n        user=url_ri.username or '',\n        pdlm=':' if url_ri.password else '',\n        passwd=url_ri.password or '',\n        udlm='@' if url_ri.username else '',\n        host=url_ri.hostname or '',\n        portdlm=':' if url_ri.port else '',\n        port=url_ri.port or '',\n    )\n    return host if protocol != 'file' else None, \\\n        url_ri.path if url_ri.path else '/', \\\n        url\n\n\ndef _ensure_version(io, base_path, version):\n    \"\"\"Check a store or dataset version and make sure it is declared\n\n    Parameters\n    ----------\n    io: SSHRemoteIO or LocalIO\n    base_path: Path\n      root path of a store or dataset\n    version: str\n      target layout version of the store (dataset tree)\n    \"\"\"\n    version_file = base_path / 'ria-layout-version'\n    if io.exists(version_file):\n        existing_version = io.read_file(version_file).split('|')[0].strip()\n        if existing_version != version.split('|')[0]:\n            # We have an already existing location with a conflicting version on\n            # record.\n            # Note, that a config flag after pipe symbol is fine.\n            raise ValueError(\"Conflicting version found at target: {}\"\n                             .format(existing_version))\n        else:\n            # already exists, recorded version fits - nothing to do\n            return\n    # Note, that the following does create the base-path dir as well, since\n    # mkdir has parents=True:\n    io.mkdir(base_path)\n    io.write_file(version_file, version)\n\n\ndef create_store(io, base_path, version):\n    \"\"\"Helper to create a RIA store\n\n    Note, that this is meant as an internal helper and part of intermediate\n    RF'ing. Ultimately should lead to dedicated command or option for\n    create-sibling-ria.\n\n    Parameters\n    ----------\n    io: SSHRemoteIO or LocalIO\n      Respective execution instance.\n      Note: To be replaced by proper command abstraction\n    base_path: Path\n      root path of the store\n    version: str\n      layout version of the store (dataset tree)\n    \"\"\"\n\n    # At store level the only version we know as of now is 1.\n    if version not in known_versions_dst:\n        raise UnknownLayoutVersion(\"RIA store layout version unknown: {}.\"\n                                   \"Supported versions: {}\"\n                                   .format(version, known_versions_dst))\n    _ensure_version(io, base_path, version)\n    error_logs = base_path / 'error_logs'\n    io.mkdir(error_logs)\n\n\ndef create_ds_in_store(io, base_path, dsid, obj_version, store_version, alias=None):\n    \"\"\"Helper to create a dataset in a RIA store\n\n    Note, that this is meant as an internal helper and part of intermediate\n    RF'ing. Ultimately should lead to a version option for create-sibling-ria\n    in conjunction with a store creation command/option.\n\n    Parameters\n    ----------\n    io: SSHRemoteIO or LocalIO\n      Respective execution instance.\n      Note: To be replaced by proper command abstraction\n    base_path: Path\n      root path of the store\n    dsid: str\n      dataset id\n    store_version: str\n      layout version of the store (dataset tree)\n    obj_version: str\n      layout version of the dataset itself (object tree)\n    alias: str, optional\n      alias for the dataset in the store\n    \"\"\"\n\n    # TODO: Note for RF'ing, that this is about setting up a valid target\n    #       for the special remote not a replacement for create-sibling-ria.\n    #       There's currently no git (bare) repo created.\n\n    try:\n        # TODO: This is currently store layout version!\n        #       Too entangled by current get_layout_locations.\n        dsgit_dir, archive_dir, dsobj_dir = \\\n            get_layout_locations(int(store_version), base_path, dsid)\n    except ValueError as e:\n        raise UnknownLayoutVersion(str(e))\n\n    if obj_version not in known_versions_objt:\n        raise UnknownLayoutVersion(\"Dataset layout version unknown: {}. \"\n                                   \"Supported: {}\"\n                                   .format(obj_version, known_versions_objt))\n\n    _ensure_version(io, dsgit_dir, obj_version)\n\n    io.mkdir(archive_dir)\n    io.mkdir(dsobj_dir)\n    if alias:\n        alias_dir = base_path / \"alias\"\n        io.mkdir(alias_dir)\n        try:\n            # go for a relative path to keep the alias links valid\n            # when moving a store\n            io.symlink(\n                Path('..') / dsgit_dir.relative_to(base_path),\n                alias_dir / alias)\n        except FileExistsError:\n            lgr.warning(\"Alias %r already exists in the RIA store, not adding an \"\n                        \"alias.\", alias)\n",
            "header": {
                "file": {
                    "path": "datalad/customremotes/ria_utils.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "e3cf01604",
                "new_commit_hash": "1e8f59ca6",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/datalad/customremotes/ria_utils.py"
            },
            "plus_file": {
                "path": "b/datalad/customremotes/ria_utils.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 85,
                            "length": 6
                        },
                        "new_range": {
                            "start": 85,
                            "length": 8
                        },
                        "section": "def verify_ria_url(url, cfg):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    -------",
                                "type": "context"
                            },
                            {
                                "content": "    tuple",
                                "type": "context"
                            },
                            {
                                "content": "      (host, base-path, rewritten url)",
                                "type": "context"
                            },
                            {
                                "content": "      `host` is not just a hostname, but is a stub URL that may also contain",
                                "type": "added"
                            },
                            {
                                "content": "      username, password, and port, if specified in a given URL.",
                                "type": "added"
                            },
                            {
                                "content": "    \"\"\"",
                                "type": "context"
                            },
                            {
                                "content": "    from datalad.config import rewrite_url",
                                "type": "context"
                            },
                            {
                                "content": "    from datalad.support.network import URL",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "datalad/customremotes/ria_utils.py",
                            "type": "function",
                            "name": "verify_ria_url",
                            "content": "def verify_ria_url(url, cfg):\n    \"\"\"Verify and decode ria url\n\n    Expects a ria-URL pointing to a RIA store, applies rewrites and tries to\n    decode potential host and base path for the store from it. Additionally\n    raises if `url` is considered invalid.\n\n    ria+ssh://somehost:/path/to/store\n    ria+file:///path/to/store\n\n    Parameters\n    ----------\n    url : str\n      URL to verify an decode.\n    cfg : dict-like\n      Configuration settings for rewrite_url()\n\n    Raises\n    ------\n    ValueError\n\n    Returns\n    -------\n    tuple\n      (host, base-path, rewritten url)\n      `host` is not just a hostname, but is a stub URL that may also contain\n      username, password, and port, if specified in a given URL.\n    \"\"\"\n    from datalad.config import rewrite_url\n    from datalad.support.network import URL\n\n    if not url:\n        raise ValueError(\"Got no URL\")\n\n    url = rewrite_url(cfg, url)\n    url_ri = URL(url)\n    if not url_ri.scheme.startswith('ria+'):\n        raise ValueError(\"Missing ria+ prefix in final URL: %s\" % url)\n    if url_ri.fragment:\n        raise ValueError(\n            \"Unexpected fragment in RIA-store URL: %s\" % url_ri.fragment)\n    protocol = url_ri.scheme[4:]\n    if protocol not in ['ssh', 'file', 'http', 'https']:\n        raise ValueError(\"Unsupported protocol: %s. \"\n                         \"Supported: ssh, file, http(s)\" %\n                         protocol)\n\n    host = '{proto}://{user}{pdlm}{passwd}{udlm}{host}{portdlm}{port}'.format(\n        proto=protocol,\n        user=url_ri.username or '',\n        pdlm=':' if url_ri.password else '',\n        passwd=url_ri.password or '',\n        udlm='@' if url_ri.username else '',\n        host=url_ri.hostname or '',\n        portdlm=':' if url_ri.port else '',\n        port=url_ri.port or '',\n    )\n    return host if protocol != 'file' else None, \\\n        url_ri.path if url_ri.path else '/', \\\n        url",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 63,
                            "end_lineno": 122,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 105,
                            "length": 7
                        },
                        "new_range": {
                            "start": 107,
                            "length": 17
                        },
                        "section": "def verify_ria_url(url, cfg):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "                         \"Supported: ssh, file, http(s)\" %",
                                "type": "context"
                            },
                            {
                                "content": "                         protocol)",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    return url_ri.hostname if protocol != 'file' else None, \\",
                                "type": "deleted"
                            },
                            {
                                "content": "    host = '{proto}://{user}{pdlm}{passwd}{udlm}{host}{portdlm}{port}'.format(",
                                "type": "added"
                            },
                            {
                                "content": "        proto=protocol,",
                                "type": "added"
                            },
                            {
                                "content": "        user=url_ri.username or '',",
                                "type": "added"
                            },
                            {
                                "content": "        pdlm=':' if url_ri.password else '',",
                                "type": "added"
                            },
                            {
                                "content": "        passwd=url_ri.password or '',",
                                "type": "added"
                            },
                            {
                                "content": "        udlm='@' if url_ri.username else '',",
                                "type": "added"
                            },
                            {
                                "content": "        host=url_ri.hostname or '',",
                                "type": "added"
                            },
                            {
                                "content": "        portdlm=':' if url_ri.port else '',",
                                "type": "added"
                            },
                            {
                                "content": "        port=url_ri.port or '',",
                                "type": "added"
                            },
                            {
                                "content": "    )",
                                "type": "added"
                            },
                            {
                                "content": "    return host if protocol != 'file' else None, \\",
                                "type": "added"
                            },
                            {
                                "content": "        url_ri.path if url_ri.path else '/', \\",
                                "type": "context"
                            },
                            {
                                "content": "        url",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "datalad/customremotes/ria_utils.py",
                            "type": "function",
                            "name": "verify_ria_url",
                            "content": "def verify_ria_url(url, cfg):\n    \"\"\"Verify and decode ria url\n\n    Expects a ria-URL pointing to a RIA store, applies rewrites and tries to\n    decode potential host and base path for the store from it. Additionally\n    raises if `url` is considered invalid.\n\n    ria+ssh://somehost:/path/to/store\n    ria+file:///path/to/store\n\n    Parameters\n    ----------\n    url : str\n      URL to verify an decode.\n    cfg : dict-like\n      Configuration settings for rewrite_url()\n\n    Raises\n    ------\n    ValueError\n\n    Returns\n    -------\n    tuple\n      (host, base-path, rewritten url)\n    \"\"\"\n    from datalad.config import rewrite_url\n    from datalad.support.network import URL\n\n    if not url:\n        raise ValueError(\"Got no URL\")\n\n    url = rewrite_url(cfg, url)\n    url_ri = URL(url)\n    if not url_ri.scheme.startswith('ria+'):\n        raise ValueError(\"Missing ria+ prefix in final URL: %s\" % url)\n    if url_ri.fragment:\n        raise ValueError(\n            \"Unexpected fragment in RIA-store URL: %s\" % url_ri.fragment)\n    protocol = url_ri.scheme[4:]\n    if protocol not in ['ssh', 'file', 'http', 'https']:\n        raise ValueError(\"Unsupported protocol: %s. \"\n                         \"Supported: ssh, file, http(s)\" %\n                         protocol)\n\n    return url_ri.hostname if protocol != 'file' else None, \\\n        url_ri.path if url_ri.path else '/', \\\n        url",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 63,
                            "end_lineno": 110,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "from datalad.distributed.ora_remote import (\n    LocalIO,\n    SSHRemoteIO\n)\nfrom datalad.customremotes.ria_utils import (\n    create_store,\n    create_ds_in_store,\n    UnknownLayoutVersion\n)\nfrom datalad.utils import (\n    on_windows,\n    Path,\n)\nfrom datalad.tests.utils import (\n    assert_equal,\n    assert_raises,\n    assert_true,\n    rmtree,\n    skip_ssh,\n    with_tempfile,\n    SkipTest,\n)\n\n\n@with_tempfile\ndef _test_setup_store(io_cls, io_args, store):\n    io = io_cls(*io_args)\n    store = Path(store)\n    version_file = store / 'ria-layout-version'\n    error_logs = store / 'error_logs'\n\n    # invalid version raises:\n    assert_raises(UnknownLayoutVersion, create_store, io, store, '2')\n\n    # non-existing path should work:\n    create_store(io, store, '1')\n    assert_true(version_file.exists())\n    assert_true(error_logs.exists())\n    assert_true(error_logs.is_dir())\n    assert_equal([f for f in error_logs.iterdir()], [])\n\n    # empty target directory should work as well:\n    rmtree(str(store))\n    store.mkdir(exist_ok=False)\n    create_store(io, store, '1')\n    assert_true(version_file.exists())\n    assert_true(error_logs.exists())\n    assert_true(error_logs.is_dir())\n    assert_equal([f for f in error_logs.iterdir()], [])\n\n    # re-execution also fine:\n    create_store(io, store, '1')\n\n    # but version conflict with existing target isn't:\n    version_file.write_text(\"2|unknownflags\\n\")\n    assert_raises(ValueError, create_store, io, store, '1')\n    # TODO: check output reporting conflicting version \"2\"\n\n\ndef test_setup_store():\n\n    yield _test_setup_store, LocalIO, []\n\n    if on_windows:\n        raise SkipTest('ora_remote.SSHRemoteIO stalls on Windows')\n\n    yield skip_ssh(_test_setup_store), SSHRemoteIO, ['datalad-test']\n\n\n@with_tempfile\ndef _test_setup_ds_in_store(io_cls, io_args, store):\n    io = io_cls(*io_args)\n    store = Path(store)\n    # ATM create_ds_in_store doesn't care what kind of ID is provided\n    dsid = \"abc123456\"\n\n    ds_path = store / dsid[:3] / dsid[3:]  # store layout version 1\n    version_file = ds_path / 'ria-layout-version'\n    archives = ds_path / 'archives'\n    objects = ds_path / 'annex' / 'objects'\n    git_config = ds_path / 'config'\n\n    # invalid store version:\n    assert_raises(UnknownLayoutVersion,\n                  create_ds_in_store, io, store, dsid, '1', 'abc')\n\n    # invalid obj version:\n    assert_raises(UnknownLayoutVersion,\n                  create_ds_in_store, io, store, dsid, 'abc', '1')\n\n    # version 1\n    create_store(io, store, '1')\n    create_ds_in_store(io, store, dsid, '1', '1')\n    for p in [ds_path, archives, objects]:\n        assert_true(p.is_dir(), msg=\"Not a directory: %s\" % str(p))\n    for p in [version_file]:\n        assert_true(p.is_file(), msg=\"Not a file: %s\" % str(p))\n    assert_equal(version_file.read_text(), \"1\\n\")\n\n    # conflicting version exists at target:\n    assert_raises(ValueError, create_ds_in_store, io, store, dsid, '2', '1')\n\n    # version 2\n    # Note: The only difference between version 1 and 2 are supposed to be the\n    #       key paths (dirhashlower vs mixed), which has nothing to do with\n    #       setup routine.\n    rmtree(str(store))\n    create_store(io, store, '1')\n    create_ds_in_store(io, store, dsid, '2', '1')\n    for p in [ds_path, archives, objects]:\n        assert_true(p.is_dir(), msg=\"Not a directory: %s\" % str(p))\n    for p in [version_file]:\n        assert_true(p.is_file(), msg=\"Not a file: %s\" % str(p))\n    assert_equal(version_file.read_text(), \"2\\n\")\n\n\ndef test_setup_ds_in_store():\n\n    yield _test_setup_ds_in_store, LocalIO, []\n\n    if on_windows:\n        raise SkipTest('ora_remote.SSHRemoteIO stalls on Windows')\n\n    yield skip_ssh(_test_setup_ds_in_store), SSHRemoteIO, ['datalad-test']\n",
            "new_file_content": "from datalad.distributed.ora_remote import (\n    LocalIO,\n    SSHRemoteIO\n)\nfrom datalad.customremotes.ria_utils import (\n    create_store,\n    create_ds_in_store,\n    verify_ria_url,\n    UnknownLayoutVersion\n)\nfrom datalad.utils import (\n    on_windows,\n    Path,\n)\nfrom datalad.tests.utils import (\n    assert_equal,\n    assert_raises,\n    assert_true,\n    rmtree,\n    skip_ssh,\n    with_tempfile,\n    SkipTest,\n)\n\n\n@with_tempfile\ndef _test_setup_store(io_cls, io_args, store):\n    io = io_cls(*io_args)\n    store = Path(store)\n    version_file = store / 'ria-layout-version'\n    error_logs = store / 'error_logs'\n\n    # invalid version raises:\n    assert_raises(UnknownLayoutVersion, create_store, io, store, '2')\n\n    # non-existing path should work:\n    create_store(io, store, '1')\n    assert_true(version_file.exists())\n    assert_true(error_logs.exists())\n    assert_true(error_logs.is_dir())\n    assert_equal([f for f in error_logs.iterdir()], [])\n\n    # empty target directory should work as well:\n    rmtree(str(store))\n    store.mkdir(exist_ok=False)\n    create_store(io, store, '1')\n    assert_true(version_file.exists())\n    assert_true(error_logs.exists())\n    assert_true(error_logs.is_dir())\n    assert_equal([f for f in error_logs.iterdir()], [])\n\n    # re-execution also fine:\n    create_store(io, store, '1')\n\n    # but version conflict with existing target isn't:\n    version_file.write_text(\"2|unknownflags\\n\")\n    assert_raises(ValueError, create_store, io, store, '1')\n    # TODO: check output reporting conflicting version \"2\"\n\n\ndef test_setup_store():\n\n    yield _test_setup_store, LocalIO, []\n\n    if on_windows:\n        raise SkipTest('ora_remote.SSHRemoteIO stalls on Windows')\n\n    yield skip_ssh(_test_setup_store), SSHRemoteIO, ['datalad-test']\n\n\n@with_tempfile\ndef _test_setup_ds_in_store(io_cls, io_args, store):\n    io = io_cls(*io_args)\n    store = Path(store)\n    # ATM create_ds_in_store doesn't care what kind of ID is provided\n    dsid = \"abc123456\"\n\n    ds_path = store / dsid[:3] / dsid[3:]  # store layout version 1\n    version_file = ds_path / 'ria-layout-version'\n    archives = ds_path / 'archives'\n    objects = ds_path / 'annex' / 'objects'\n    git_config = ds_path / 'config'\n\n    # invalid store version:\n    assert_raises(UnknownLayoutVersion,\n                  create_ds_in_store, io, store, dsid, '1', 'abc')\n\n    # invalid obj version:\n    assert_raises(UnknownLayoutVersion,\n                  create_ds_in_store, io, store, dsid, 'abc', '1')\n\n    # version 1\n    create_store(io, store, '1')\n    create_ds_in_store(io, store, dsid, '1', '1')\n    for p in [ds_path, archives, objects]:\n        assert_true(p.is_dir(), msg=\"Not a directory: %s\" % str(p))\n    for p in [version_file]:\n        assert_true(p.is_file(), msg=\"Not a file: %s\" % str(p))\n    assert_equal(version_file.read_text(), \"1\\n\")\n\n    # conflicting version exists at target:\n    assert_raises(ValueError, create_ds_in_store, io, store, dsid, '2', '1')\n\n    # version 2\n    # Note: The only difference between version 1 and 2 are supposed to be the\n    #       key paths (dirhashlower vs mixed), which has nothing to do with\n    #       setup routine.\n    rmtree(str(store))\n    create_store(io, store, '1')\n    create_ds_in_store(io, store, dsid, '2', '1')\n    for p in [ds_path, archives, objects]:\n        assert_true(p.is_dir(), msg=\"Not a directory: %s\" % str(p))\n    for p in [version_file]:\n        assert_true(p.is_file(), msg=\"Not a file: %s\" % str(p))\n    assert_equal(version_file.read_text(), \"2\\n\")\n\n\ndef test_setup_ds_in_store():\n\n    yield _test_setup_ds_in_store, LocalIO, []\n\n    if on_windows:\n        raise SkipTest('ora_remote.SSHRemoteIO stalls on Windows')\n\n    yield skip_ssh(_test_setup_ds_in_store), SSHRemoteIO, ['datalad-test']\n\n\ndef test_verify_ria_url():\n    # unsupported protocol\n    assert_raises(ValueError,\n                  verify_ria_url, 'ria+ftp://localhost/tmp/this', {})\n    # bunch of caes that should work\n    cases = {\n        'ria+file:///tmp/this': (None, '/tmp/this'),\n        # no normalization\n        'ria+file:///tmp/this/': (None, '/tmp/this/'),\n        # with hosts\n        'ria+ssh://localhost/tmp/this': ('ssh://localhost', '/tmp/this'),\n        'ria+http://localhost/tmp/this': ('http://localhost', '/tmp/this'),\n        'ria+https://localhost/tmp/this': ('https://localhost', '/tmp/this'),\n        # with username\n        'ria+ssh://humbug@localhost/tmp/this':\n            ('ssh://humbug@localhost', '/tmp/this'),\n        # with port\n        'ria+ssh://humbug@localhost:2222/tmp/this':\n            ('ssh://humbug@localhost:2222', '/tmp/this'),\n        'ria+ssh://localhost:2200/tmp/this':\n            ('ssh://localhost:2200', '/tmp/this'),\n        # with password\n        'ria+https://humbug:1234@localhost:8080/tmp/this':\n            ('https://humbug:1234@localhost:8080', '/tmp/this'),\n        # document a strange (MIH thinks undesirable), but pre-existing\n        # behavior an 'ssh example.com' would end up in the user HOME,\n        # not in '/'\n        'ria+ssh://example.com': ('ssh://example.com', '/')\n    }\n    for i, o in cases.items():\n        # we are not testing the URL rewriting here\n        assert_equal(o, verify_ria_url(i, {})[:2])\n",
            "header": {
                "file": {
                    "path": "datalad/customremotes/tests/test_ria_utils.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "03c6c5a65",
                "new_commit_hash": "4817b9344",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/datalad/customremotes/tests/test_ria_utils.py"
            },
            "plus_file": {
                "path": "b/datalad/customremotes/tests/test_ria_utils.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 5,
                            "length": 6
                        },
                        "new_range": {
                            "start": 5,
                            "length": 7
                        },
                        "section": "from datalad.distributed.ora_remote import ("
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "from datalad.customremotes.ria_utils import (",
                                "type": "context"
                            },
                            {
                                "content": "    create_store,",
                                "type": "context"
                            },
                            {
                                "content": "    create_ds_in_store,",
                                "type": "context"
                            },
                            {
                                "content": "    verify_ria_url,",
                                "type": "added"
                            },
                            {
                                "content": "    UnknownLayoutVersion",
                                "type": "context"
                            },
                            {
                                "content": ")",
                                "type": "context"
                            },
                            {
                                "content": "from datalad.utils import (",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [
                        {
                            "file_name": "datalad/customremotes/tests/test_ria_utils.py",
                            "type": "statement",
                            "name": "",
                            "content": "from datalad.customremotes.ria_utils import (\n    create_store,\n    create_ds_in_store,\n    verify_ria_url,\n    UnknownLayoutVersion\n)",
                            "ast_type_str": "ImportFrom",
                            "start_lineno": 5,
                            "end_lineno": 10,
                            "parent": null
                        }
                    ],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 122,
                            "length": 3
                        },
                        "new_range": {
                            "start": 123,
                            "length": 37
                        },
                        "section": "def test_setup_ds_in_store():"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        raise SkipTest('ora_remote.SSHRemoteIO stalls on Windows')",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    yield skip_ssh(_test_setup_ds_in_store), SSHRemoteIO, ['datalad-test']",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "def test_verify_ria_url():",
                                "type": "added"
                            },
                            {
                                "content": "    # unsupported protocol",
                                "type": "added"
                            },
                            {
                                "content": "    assert_raises(ValueError,",
                                "type": "added"
                            },
                            {
                                "content": "                  verify_ria_url, 'ria+ftp://localhost/tmp/this', {})",
                                "type": "added"
                            },
                            {
                                "content": "    # bunch of caes that should work",
                                "type": "added"
                            },
                            {
                                "content": "    cases = {",
                                "type": "added"
                            },
                            {
                                "content": "        'ria+file:///tmp/this': (None, '/tmp/this'),",
                                "type": "added"
                            },
                            {
                                "content": "        # no normalization",
                                "type": "added"
                            },
                            {
                                "content": "        'ria+file:///tmp/this/': (None, '/tmp/this/'),",
                                "type": "added"
                            },
                            {
                                "content": "        # with hosts",
                                "type": "added"
                            },
                            {
                                "content": "        'ria+ssh://localhost/tmp/this': ('ssh://localhost', '/tmp/this'),",
                                "type": "added"
                            },
                            {
                                "content": "        'ria+http://localhost/tmp/this': ('http://localhost', '/tmp/this'),",
                                "type": "added"
                            },
                            {
                                "content": "        'ria+https://localhost/tmp/this': ('https://localhost', '/tmp/this'),",
                                "type": "added"
                            },
                            {
                                "content": "        # with username",
                                "type": "added"
                            },
                            {
                                "content": "        'ria+ssh://humbug@localhost/tmp/this':",
                                "type": "added"
                            },
                            {
                                "content": "            ('ssh://humbug@localhost', '/tmp/this'),",
                                "type": "added"
                            },
                            {
                                "content": "        # with port",
                                "type": "added"
                            },
                            {
                                "content": "        'ria+ssh://humbug@localhost:2222/tmp/this':",
                                "type": "added"
                            },
                            {
                                "content": "            ('ssh://humbug@localhost:2222', '/tmp/this'),",
                                "type": "added"
                            },
                            {
                                "content": "        'ria+ssh://localhost:2200/tmp/this':",
                                "type": "added"
                            },
                            {
                                "content": "            ('ssh://localhost:2200', '/tmp/this'),",
                                "type": "added"
                            },
                            {
                                "content": "        # with password",
                                "type": "added"
                            },
                            {
                                "content": "        'ria+https://humbug:1234@localhost:8080/tmp/this':",
                                "type": "added"
                            },
                            {
                                "content": "            ('https://humbug:1234@localhost:8080', '/tmp/this'),",
                                "type": "added"
                            },
                            {
                                "content": "        # document a strange (MIH thinks undesirable), but pre-existing",
                                "type": "added"
                            },
                            {
                                "content": "        # behavior an 'ssh example.com' would end up in the user HOME,",
                                "type": "added"
                            },
                            {
                                "content": "        # not in '/'",
                                "type": "added"
                            },
                            {
                                "content": "        'ria+ssh://example.com': ('ssh://example.com', '/')",
                                "type": "added"
                            },
                            {
                                "content": "    }",
                                "type": "added"
                            },
                            {
                                "content": "    for i, o in cases.items():",
                                "type": "added"
                            },
                            {
                                "content": "        # we are not testing the URL rewriting here",
                                "type": "added"
                            },
                            {
                                "content": "        assert_equal(o, verify_ria_url(i, {})[:2])",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [
                        {
                            "file_name": "datalad/customremotes/tests/test_ria_utils.py",
                            "type": "function",
                            "name": "test_verify_ria_url",
                            "content": "def test_verify_ria_url():\n    # unsupported protocol\n    assert_raises(ValueError,\n                  verify_ria_url, 'ria+ftp://localhost/tmp/this', {})\n    # bunch of caes that should work\n    cases = {\n        'ria+file:///tmp/this': (None, '/tmp/this'),\n        # no normalization\n        'ria+file:///tmp/this/': (None, '/tmp/this/'),\n        # with hosts\n        'ria+ssh://localhost/tmp/this': ('ssh://localhost', '/tmp/this'),\n        'ria+http://localhost/tmp/this': ('http://localhost', '/tmp/this'),\n        'ria+https://localhost/tmp/this': ('https://localhost', '/tmp/this'),\n        # with username\n        'ria+ssh://humbug@localhost/tmp/this':\n            ('ssh://humbug@localhost', '/tmp/this'),\n        # with port\n        'ria+ssh://humbug@localhost:2222/tmp/this':\n            ('ssh://humbug@localhost:2222', '/tmp/this'),\n        'ria+ssh://localhost:2200/tmp/this':\n            ('ssh://localhost:2200', '/tmp/this'),\n        # with password\n        'ria+https://humbug:1234@localhost:8080/tmp/this':\n            ('https://humbug:1234@localhost:8080', '/tmp/this'),\n        # document a strange (MIH thinks undesirable), but pre-existing\n        # behavior an 'ssh example.com' would end up in the user HOME,\n        # not in '/'\n        'ria+ssh://example.com': ('ssh://example.com', '/')\n    }\n    for i, o in cases.items():\n        # we are not testing the URL rewriting here\n        assert_equal(o, verify_ria_url(i, {})[:2])",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 128,
                            "end_lineno": 159,
                            "parent": null
                        }
                    ],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "22948af54d5469e5c708414a262e968ecbf593d9^",
    "new_commit_hash": "22948af54d5469e5c708414a262e968ecbf593d9",
    "commit_message": "Merge remote-tracking branch 'gh-master/maint'",
    "commit_date": "2021-08-17T09:52:18+02:00",
    "metadata": {}
}
{
    "repo_name": "datalad",
    "new_commit_hash": "22948af54d5469e5c708414a262e968ecbf593d9",
    "test_file_codes": [
        "from datalad.distributed.ora_remote import (\n    LocalIO,\n    SSHRemoteIO\n)\nfrom datalad.customremotes.ria_utils import (\n    create_store,\n    create_ds_in_store,\n    verify_ria_url,\n    UnknownLayoutVersion\n)\nfrom datalad.utils import (\n    on_windows,\n    Path,\n)\nfrom datalad.tests.utils import (\n    assert_equal,\n    assert_raises,\n    assert_true,\n    rmtree,\n    skip_ssh,\n    with_tempfile,\n    SkipTest,\n)\n\n\n@with_tempfile\ndef _test_setup_store(io_cls, io_args, store):\n    io = io_cls(*io_args)\n    store = Path(store)\n    version_file = store / 'ria-layout-version'\n    error_logs = store / 'error_logs'\n\n    # invalid version raises:\n    assert_raises(UnknownLayoutVersion, create_store, io, store, '2')\n\n    # non-existing path should work:\n    create_store(io, store, '1')\n    assert_true(version_file.exists())\n    assert_true(error_logs.exists())\n    assert_true(error_logs.is_dir())\n    assert_equal([f for f in error_logs.iterdir()], [])\n\n    # empty target directory should work as well:\n    rmtree(str(store))\n    store.mkdir(exist_ok=False)\n    create_store(io, store, '1')\n    assert_true(version_file.exists())\n    assert_true(error_logs.exists())\n    assert_true(error_logs.is_dir())\n    assert_equal([f for f in error_logs.iterdir()], [])\n\n    # re-execution also fine:\n    create_store(io, store, '1')\n\n    # but version conflict with existing target isn't:\n    version_file.write_text(\"2|unknownflags\\n\")\n    assert_raises(ValueError, create_store, io, store, '1')\n    # TODO: check output reporting conflicting version \"2\"\n\n\ndef test_setup_store():\n\n    yield _test_setup_store, LocalIO, []\n\n    if on_windows:\n        raise SkipTest('ora_remote.SSHRemoteIO stalls on Windows')\n\n    yield skip_ssh(_test_setup_store), SSHRemoteIO, ['datalad-test']\n\n\n@with_tempfile\ndef _test_setup_ds_in_store(io_cls, io_args, store):\n    io = io_cls(*io_args)\n    store = Path(store)\n    # ATM create_ds_in_store doesn't care what kind of ID is provided\n    dsid = \"abc123456\"\n\n    ds_path = store / dsid[:3] / dsid[3:]  # store layout version 1\n    version_file = ds_path / 'ria-layout-version'\n    archives = ds_path / 'archives'\n    objects = ds_path / 'annex' / 'objects'\n    git_config = ds_path / 'config'\n\n    # invalid store version:\n    assert_raises(UnknownLayoutVersion,\n                  create_ds_in_store, io, store, dsid, '1', 'abc')\n\n    # invalid obj version:\n    assert_raises(UnknownLayoutVersion,\n                  create_ds_in_store, io, store, dsid, 'abc', '1')\n\n    # version 1\n    create_store(io, store, '1')\n    create_ds_in_store(io, store, dsid, '1', '1')\n    for p in [ds_path, archives, objects]:\n        assert_true(p.is_dir(), msg=\"Not a directory: %s\" % str(p))\n    for p in [version_file]:\n        assert_true(p.is_file(), msg=\"Not a file: %s\" % str(p))\n    assert_equal(version_file.read_text(), \"1\\n\")\n\n    # conflicting version exists at target:\n    assert_raises(ValueError, create_ds_in_store, io, store, dsid, '2', '1')\n\n    # version 2\n    # Note: The only difference between version 1 and 2 are supposed to be the\n    #       key paths (dirhashlower vs mixed), which has nothing to do with\n    #       setup routine.\n    rmtree(str(store))\n    create_store(io, store, '1')\n    create_ds_in_store(io, store, dsid, '2', '1')\n    for p in [ds_path, archives, objects]:\n        assert_true(p.is_dir(), msg=\"Not a directory: %s\" % str(p))\n    for p in [version_file]:\n        assert_true(p.is_file(), msg=\"Not a file: %s\" % str(p))\n    assert_equal(version_file.read_text(), \"2\\n\")\n\n\ndef test_setup_ds_in_store():\n\n    yield _test_setup_ds_in_store, LocalIO, []\n\n    if on_windows:\n        raise SkipTest('ora_remote.SSHRemoteIO stalls on Windows')\n\n    yield skip_ssh(_test_setup_ds_in_store), SSHRemoteIO, ['datalad-test']\n\n\ndef test_verify_ria_url():\n    # unsupported protocol\n    assert_raises(ValueError,\n                  verify_ria_url, 'ria+ftp://localhost/tmp/this', {})\n    # bunch of caes that should work\n    cases = {\n        'ria+file:///tmp/this': (None, '/tmp/this'),\n        # no normalization\n        'ria+file:///tmp/this/': (None, '/tmp/this/'),\n        # with hosts\n        'ria+ssh://localhost/tmp/this': ('ssh://localhost', '/tmp/this'),\n        'ria+http://localhost/tmp/this': ('http://localhost', '/tmp/this'),\n        'ria+https://localhost/tmp/this': ('https://localhost', '/tmp/this'),\n        # with username\n        'ria+ssh://humbug@localhost/tmp/this':\n            ('ssh://humbug@localhost', '/tmp/this'),\n        # with port\n        'ria+ssh://humbug@localhost:2222/tmp/this':\n            ('ssh://humbug@localhost:2222', '/tmp/this'),\n        'ria+ssh://localhost:2200/tmp/this':\n            ('ssh://localhost:2200', '/tmp/this'),\n        # with password\n        'ria+https://humbug:1234@localhost:8080/tmp/this':\n            ('https://humbug:1234@localhost:8080', '/tmp/this'),\n        # document a strange (MIH thinks undesirable), but pre-existing\n        # behavior an 'ssh example.com' would end up in the user HOME,\n        # not in '/'\n        'ria+ssh://example.com': ('ssh://example.com', '/')\n    }\n    for i, o in cases.items():\n        # we are not testing the URL rewriting here\n        assert_equal(o, verify_ria_url(i, {})[:2])\n",
        "# emacs: -*- mode: python-mode; 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 functioning of the datalad main cmdline utility \"\"\"\n\nimport os\nimport re\nfrom io import StringIO\nfrom unittest.mock import patch\n\nimport datalad\nfrom datalad.cmdline.main import (\n    _fix_datalad_ri,\n    main,\n)\nfrom datalad.cmdline.helpers import fail_with_short_help\nfrom datalad import __version__\nfrom datalad.cmd import (\n    WitlessRunner as Runner,\n    StdOutErrCapture,\n)\nfrom datalad.ui.utils import (\n    get_console_width,\n    get_terminal_size,\n)\nfrom datalad.api import (\n    create,\n    Dataset,\n)\nfrom datalad.utils import (\n    chpwd,\n    Path,\n)\nfrom datalad.tests.utils import (\n    SkipTest,\n    assert_equal,\n    assert_in,\n    assert_not_in,\n    assert_raises,\n    assert_re_in,\n    eq_,\n    in_,\n    ok_startswith,\n    on_windows,\n    slow,\n    with_tempfile,\n)\nfrom datalad.support.exceptions import CommandError\n\n\ndef run_main(args, exit_code=0, expect_stderr=False):\n    \"\"\"Run main() of the datalad, do basic checks and provide outputs\n\n    Parameters\n    ----------\n    args : list\n        List of string cmdline arguments to pass\n    exit_code : int\n        Expected exit code. Would raise AssertionError if differs\n    expect_stderr : bool or string\n        Whether to expect stderr output. If string -- match\n\n    Returns\n    -------\n    stdout, stderr  strings\n       Output produced\n    \"\"\"\n    with patch('sys.stderr', new_callable=StringIO) as cmerr:\n        with patch('sys.stdout', new_callable=StringIO) as cmout:\n            with assert_raises(SystemExit) as cm:\n                main([\"datalad\"] + list(args))\n            assert_equal(cm.exception.code, exit_code)\n            stdout = cmout.getvalue()\n            stderr = cmerr.getvalue()\n            if expect_stderr is False:\n                assert_equal(stderr, \"\")\n            elif expect_stderr is True:\n                # do nothing -- just return\n                pass\n            else:\n                # must be a string\n                assert_equal(stderr, expect_stderr)\n    return stdout, stderr\n\n\n# TODO: switch to stdout for --version output\ndef test_version():\n    # we just get a version if not asking for a version of some command\n    stdout, stderr = run_main(['--version'], expect_stderr=True)\n    eq_(stdout.rstrip(), \"datalad %s\" % datalad.__version__)\n\n    stdout, stderr = run_main(['clone', '--version'], expect_stderr=True)\n    ok_startswith(stdout, 'datalad %s\\n' % datalad.__version__)\n    # since https://github.com/datalad/datalad/pull/2733 no license in --version\n    assert_not_in(\"Copyright\", stdout)\n    assert_not_in(\"Permission is hereby granted\", stdout)\n\n    try:\n        import datalad_container\n    except ImportError:\n        pass  # not installed, cannot test with extension\n    else:\n        stdout, stderr = run_main(['containers-list', '--version'], expect_stderr=True)\n        eq_(stdout, 'datalad_container %s\\n' % datalad_container.__version__)\n\n\ndef test_help_np():\n    stdout, stderr = run_main(['--help-np'])\n\n    # Let's extract section titles:\n    # enough of bin/datalad and .tox/py27/bin/datalad -- guarantee consistency! ;)\n    ok_startswith(stdout, 'Usage: datalad')\n    # Sections start/end with * if ran under DATALAD_HELP2MAN mode\n    sections = [l[1:-1] for l in filter(re.compile('^\\*.*\\*$').match, stdout.split('\\n'))]\n    # but order is still not guaranteed (dict somewhere)! TODO\n    # see https://travis-ci.org/datalad/datalad/jobs/80519004\n    # thus testing sets\n    for s in {'Commands for dataset operations',\n              'Commands for metadata handling',\n              'Miscellaneous commands',\n              'General information',\n              'Global options',\n              'Plumbing commands',\n              }:\n        assert_in(s, sections)\n\n    if not get_terminal_size()[0] or 0:\n        raise SkipTest(\n            \"Could not determine terminal size, skipping the rest of the test\")\n\n    # none of the lines must be longer than 80 chars\n    # TODO: decide on   create-sibling and possibly\n    # rewrite-urls\n    accepted_width = get_console_width()\n\n    long_lines = [\"%d %s\" % (len(l), l) for l in stdout.split('\\n')\n                  if len(l) > accepted_width and\n                  '{' not in l  # on nd70 summary line is unsplit\n                  ]\n    if long_lines:\n        raise AssertionError(\n            \"Following lines in --help output were longer than %s chars:\\n%s\"\n            % (accepted_width, '\\n'.join(long_lines))\n        )\n\n\ndef test_usage_on_insufficient_args():\n    stdout, stderr = run_main(['install'], exit_code=2, expect_stderr=True)\n    ok_startswith(stderr, 'usage:')\n\n\ndef test_subcmd_usage_on_unknown_args():\n    stdout, stderr = run_main(['get', '--murks'], exit_code=1, expect_stderr=True)\n    in_('get', stdout)\n\n\ndef test_combined_short_option():\n    stdout, stderr = run_main(['-fjson'], exit_code=2, expect_stderr=True)\n    assert_not_in(\"unrecognized argument\", stderr)\n    assert_in(\"too few arguments\", stderr)\n\n\ndef check_incorrect_option(opts, err_str):\n    # The first line used to be:\n    # stdout, stderr = run_main((sys.argv[0],) + opts, expect_stderr=True, exit_code=2)\n    # But: what do we expect to be in sys.argv[0] here?\n    # It depends on how we invoke the test.\n    # - nosetests -s -v datalad/cmdline/tests/test_main.py would result in:\n    #   sys.argv[0}=='nosetests'\n    # - python -m nose -s -v datalad/cmdline/tests/test_main.py would result in:\n    #   sys.argv[0}=='python -m nose'\n    # - python -c \"import nose; nose.main()\" -s -v datalad/cmdline/tests/test_main.py would result in:\n    #   sys.argv[0]=='-c'\n    # This led to failure in case sys.argv[0] contained an option, that was\n    # defined to be a datalad option too, therefore was a 'known_arg' and was\n    # checked to meet its constraints.\n    # But sys.argv[0] actually isn't used by main at all. It simply doesn't\n    # matter what's in there. The only thing important to pass here is `opts`.\n    stdout, stderr = run_main(opts, expect_stderr=True, exit_code=2)\n    out = stdout + stderr\n    assert_in(\"usage: \", out)\n    assert_re_in(err_str, out, match=False)\n\n\ndef test_incorrect_options():\n    # apparently a bit different if following a good one so let's do both\n    err_invalid = \"error: (invalid|too few arguments|unrecognized argument)\"\n    yield check_incorrect_option, ('--buga',), err_invalid\n    yield check_incorrect_option, ('--dbg', '--buga'), err_invalid\n\n    err_insufficient = err_invalid  # \"specify\"\n    yield check_incorrect_option, ('--dbg',), err_insufficient\n    yield check_incorrect_option, tuple(), err_insufficient\n\n\ndef test_script_shims():\n    runner = Runner()\n    for script in [\n        'datalad',\n        'git-annex-remote-datalad-archives',\n        'git-annex-remote-datalad']:\n        if not on_windows:\n            # those must be available for execution, and should not contain\n            which = runner.run(['which', script], protocol=StdOutErrCapture)['stdout']\n            # test if there is no easy install shim in there\n            with open(which.rstrip()) as f:\n                content = f.read()\n        else:\n            from distutils.spawn import find_executable\n            content = find_executable(script)\n\n        # and let's check that it is our script\n        out = runner.run([script, '--version'], protocol=StdOutErrCapture)\n        version = out['stdout'].rstrip()\n        mod, version = version.split(' ', 1)\n        assert_equal(mod, 'datalad')\n        # we can get git and non git .dev version... so for now\n        # relax\n        get_numeric_portion = lambda v: [x for x in re.split('[+.]', v) if x.isdigit()]\n        # extract numeric portion\n        assert get_numeric_portion(version), f\"Got no numeric portion from {version}\"\n        assert_equal(get_numeric_portion(__version__),\n                     get_numeric_portion(version))\n\n\n@slow  # 11.2591s\n@with_tempfile(mkdir=True)\ndef test_cfg_override(path):\n    with chpwd(path):\n        cmd = ['datalad', 'wtf', '-s', 'some']\n        # control\n        out = Runner().run(cmd, protocol=StdOutErrCapture)['stdout']\n        assert_not_in('datalad.dummy: this', out)\n        # ensure that this is not a dataset's cfg manager\n        assert_not_in('datalad.dataset.id', out)\n        # env var\n        out = Runner(env=dict(os.environ, DATALAD_DUMMY='this')).run(\n            cmd, protocol=StdOutErrCapture)['stdout']\n        assert_in('datalad.dummy: this', out)\n        # cmdline arg\n        out = Runner().run([cmd[0], '-c', 'datalad.dummy=this'] + cmd[1:],\n                           protocol=StdOutErrCapture)['stdout']\n        assert_in('datalad.dummy: this', out)\n\n        # now create a dataset in the path. the wtf plugin will switch to\n        # using the dataset's config manager, which must inherit the overrides\n        create(dataset=path)\n        # control\n        out = Runner().run(cmd, protocol=StdOutErrCapture)['stdout']\n        assert_not_in('datalad.dummy: this', out)\n        # ensure that this is a dataset's cfg manager\n        assert_in('datalad.dataset.id', out)\n        # env var\n        out = Runner(env=dict(os.environ, DATALAD_DUMMY='this')).run(\n            cmd, protocol=StdOutErrCapture)['stdout']\n        assert_in('datalad.dummy: this', out)\n        # cmdline arg\n        out = Runner().run([cmd[0], '-c', 'datalad.dummy=this'] + cmd[1:],\n                           protocol=StdOutErrCapture)['stdout']\n        assert_in('datalad.dummy: this', out)\n\n\ndef test_incorrect_cfg_override():\n    run_main(['-c', 'some', 'wtf'], exit_code=3)\n    run_main(['-c', 'some=', 'wtf'], exit_code=3)\n    run_main(['-c', 'some.var', 'wtf'], exit_code=3)\n    run_main(['-c', 'some.var=', 'wtf'], exit_code=3)\n\n\ndef test_fail_with_short_help():\n    out = StringIO()\n    with assert_raises(SystemExit) as cme:\n        fail_with_short_help(exit_code=3, out=out)\n    assert_equal(cme.exception.code, 3)\n    assert_equal(out.getvalue(), \"\")\n\n    out = StringIO()\n    with assert_raises(SystemExit) as cme:\n        fail_with_short_help(msg=\"Failed badly\", out=out)\n    assert_equal(cme.exception.code, 1)\n    assert_equal(out.getvalue(), \"error: Failed badly\\n\")\n\n    # Suggestions, hint, etc\n    out = StringIO()\n    with assert_raises(SystemExit) as cme:\n        fail_with_short_help(\n            msg=\"Failed badly\",\n            known=[\"mother\", \"mutter\", \"father\", \"son\"],\n            provided=\"muther\",\n            hint=\"You can become one\",\n            exit_code=0,  # nobody forbids\n            what=\"parent\",\n            out=out)\n    assert_equal(cme.exception.code, 0)\n    assert_equal(out.getvalue(),\n                 \"error: Failed badly\\n\"\n                 \"datalad: Unknown parent 'muther'.  See 'datalad --help'.\\n\\n\"\n                 \"Did you mean any of these?\\n\"\n                 \"        mutter\\n\"\n                 \"        mother\\n\"\n                 \"        father\\n\"\n                 \"Hint: You can become one\\n\")\n\ndef test_fix_datalad_ri():\n    assert_equal(_fix_datalad_ri('/'), '/')\n    assert_equal(_fix_datalad_ri('/a/b'), '/a/b')\n    assert_equal(_fix_datalad_ri('//'), '///')\n    assert_equal(_fix_datalad_ri('///'), '///')\n    assert_equal(_fix_datalad_ri('//a'), '///a')\n    assert_equal(_fix_datalad_ri('///a'), '///a')\n    assert_equal(_fix_datalad_ri('//a/b'), '///a/b')\n    assert_equal(_fix_datalad_ri('///a/b'), '///a/b')\n\n\ndef test_fail_with_short_help():\n    out = StringIO()\n    with assert_raises(SystemExit) as cme:\n        fail_with_short_help(exit_code=3, out=out)\n    assert_equal(cme.exception.code, 3)\n    assert_equal(out.getvalue(), \"\")\n\n    out = StringIO()\n    with assert_raises(SystemExit) as cme:\n        fail_with_short_help(msg=\"Failed badly\", out=out)\n    assert_equal(cme.exception.code, 1)\n    assert_equal(out.getvalue(), \"error: Failed badly\\n\")\n\n    # Suggestions, hint, etc\n    out = StringIO()\n    with assert_raises(SystemExit) as cme:\n        fail_with_short_help(\n            msg=\"Failed badly\",\n            known=[\"mother\", \"mutter\", \"father\", \"son\"],\n            provided=\"muther\",\n            hint=\"You can become one\",\n            exit_code=0,  # nobody forbids\n            what=\"parent\",\n            out=out)\n    assert_equal(cme.exception.code, 0)\n    assert_equal(out.getvalue(),\n                 \"error: Failed badly\\n\"\n                 \"datalad: Unknown parent 'muther'.  See 'datalad --help'.\\n\\n\"\n                 \"Did you mean any of these?\\n\"\n                 \"        mutter\\n\"\n                 \"        mother\\n\"\n                 \"        father\\n\"\n                 \"Hint: You can become one\\n\")\n\n\ndef test_fix_datalad_ri():\n    assert_equal(_fix_datalad_ri('/'), '/')\n    assert_equal(_fix_datalad_ri('/a/b'), '/a/b')\n    assert_equal(_fix_datalad_ri('//'), '///')\n    assert_equal(_fix_datalad_ri('///'), '///')\n    assert_equal(_fix_datalad_ri('//a'), '///a')\n    assert_equal(_fix_datalad_ri('///a'), '///a')\n    assert_equal(_fix_datalad_ri('//a/b'), '///a/b')\n    assert_equal(_fix_datalad_ri('///a/b'), '///a/b')\n\n\n@with_tempfile\n@with_tempfile(mkdir=True)\ndef test_commanderror_jsonmsgs(src, exp):\n    ds = Dataset(src).create()\n    (ds.pathobj / '123').write_text('123')\n    ds.save()\n    ds.repo.call_annex([\n        'initremote', 'expdir', 'type=directory',\n        'directory={}'.format(exp),\n        'encryption=none',\n        'exporttree=yes'\n    ])\n    #ds.repo.call_annex(['export', '--to=expdir', 'HEAD'])\n    # this must fail, because `push` cannot handle an export.\n    # when https://github.com/datalad/datalad/issues/3127 is implemented\n    # this test must be adjusted\n    with assert_raises(CommandError) as cme:\n        Runner(cwd=ds.path).run(\n            ['datalad', 'push', '--to', 'expdir'],\n            protocol=StdOutErrCapture)\n    if ds.repo.git_annex_version >= \"8.20201129\":\n        in_('use `git-annex export`', cme.exception.stderr)\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",
        "test_2.py",
        "conftest.py"
    ],
    "setup_res_code": 0,
    "setup_res_stdout": "Starting Datalad installation attempts...\nVerifying installation...\nDatalad version: 0.14.7+563.g22948af54\n✅ Installation successful!\nSuccessfully installed Datalad using Python 3.9\n",
    "setup_res_stderr": "Using CPython 3.9.21\nCreating virtual environment at: .venv\nActivate with: source .venv/bin/activate\nResolved 25 packages in 67ms\nInstalled 25 packages in 2.86s\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\nResolved 65 packages in 4.99s\nPrepared 1 package in 4.07s\nInstalled 59 packages in 471ms\n + annexremote==1.6.6\n + appdirs==1.4.4\n + argcomplete==3.5.3\n + backports-tarfile==1.2.0\n + beautifulsoup4==4.12.3\n + boto==2.49.0\n + certifi==2024.12.14\n + cffi==1.17.1\n + chardet==5.2.0\n + charset-normalizer==3.4.1\n + citeproc-py==0.6.0\n + cryptography==44.0.0\n + datalad==0.14.7+563.g22948af54 (from file:///home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_22948af54d5469e5c708414a262e968ecbf593d9)\n + deprecated==1.2.15\n + distro==1.9.0\n + duecredit==0.10.2\n + exifread==3.0.0\n + fasteners==0.19\n + httpretty==1.1.4\n + humanize==4.11.0\n + idna==3.10\n + importlib-metadata==8.5.0\n + iso8601==2.1.0\n + jaraco-classes==3.4.0\n + jaraco-context==6.0.1\n + jaraco-functools==4.1.0\n + jeepney==0.8.0\n + keyring==25.6.0\n + keyrings-alt==5.0.2\n + looseversion==1.3.0\n + lxml==5.3.0\n + more-itertools==10.5.0\n + msgpack==1.1.0\n + multidict==6.1.0\n + mutagen==1.47.0\n + nose==1.3.7\n + patool==1.15.0\n + pillow==11.0.0\n + propcache==0.2.1\n + pycparser==2.22\n + pygithub==2.5.0\n + pyjwt==2.10.1\n + pynacl==1.5.0\n + pyperclip==1.9.0\n + python-gitlab==5.3.0\n + python-xmp-toolkit==2.0.2\n + pyyaml==6.0.2\n + requests==2.32.3\n + requests-ftp==0.3.1\n + requests-toolbelt==1.0.0\n + secretstorage==3.3.3\n + simplejson==3.19.3\n + soupsieve==2.6\n + tqdm==4.67.1\n + urllib3==1.26.20\n + vcrpy==7.0.0\n + whoosh==2.7.4\n + wrapt==1.17.0\n + yarl==1.18.3\nResolved 99 packages in 207ms\nInstalled 32 packages in 613ms\n + alabaster==0.7.16\n + asv==0.6.4\n + asv-runner==0.2.1\n + babel==2.16.0\n + build==1.2.2.post1\n + distlib==0.3.9\n + docutils==0.21.2\n + filelock==3.16.1\n + gprof2dot==2024.6.6\n + imagesize==1.4.1\n + jinja2==3.1.5\n + json5==0.10.0\n + markupsafe==3.0.2\n + nose-timer==1.0.1\n + platformdirs==4.3.6\n + psutil==6.1.1\n + pygments==2.18.0\n + pympler==1.1\n + pypandoc==1.14\n + pyproject-hooks==1.2.0\n + snowballstemmer==2.2.0\n + sphinx==7.4.7\n + sphinx-rtd-theme==3.0.2\n + sphinxcontrib-applehelp==2.0.0\n + sphinxcontrib-devhelp==2.0.0\n + sphinxcontrib-htmlhelp==2.1.0\n + sphinxcontrib-jquery==4.1\n + sphinxcontrib-jsmath==1.0.1\n + sphinxcontrib-qthelp==2.0.0\n + sphinxcontrib-serializinghtml==2.0.0\n + tabulate==0.9.0\n + virtualenv==20.28.0\n",
    "new_commit_res_code": 1,
    "new_commit_res_stdout": "============================= test session starts ==============================\nplatform linux -- Python 3.9.21, pytest-8.3.4, pluggy-1.5.0\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_22948af54d5469e5c708414a262e968ecbf593d9\nconfigfile: pyproject.toml\nplugins: cov-6.0.0\ncollected 15 items\n\nr2e_tests/test_1.py xx.                                                  [ 20%]\nr2e_tests/test_2.py .s...xFF...E                                         [100%]\n\n==================================== ERRORS ====================================\n_________________ ERROR at setup of test_commanderror_jsonmsgs _________________\nfile /home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_22948af54d5469e5c708414a262e968ecbf593d9/r2e_tests/test_2.py, line 366\n  @with_tempfile\n  @with_tempfile(mkdir=True)\n  def test_commanderror_jsonmsgs(src, exp):\nE       fixture 'src' 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_22948af54d5469e5c708414a262e968ecbf593d9/r2e_tests/test_2.py:366\n=================================== FAILURES ===================================\n______________________________ test_script_shims _______________________________\n\n    def test_script_shims():\n        runner = Runner()\n        for script in [\n            'datalad',\n            'git-annex-remote-datalad-archives',\n            'git-annex-remote-datalad']:\n            if not on_windows:\n                # those must be available for execution, and should not contain\n                which = runner.run(['which', script], protocol=StdOutErrCapture)['stdout']\n                # test if there is no easy install shim in there\n                with open(which.rstrip()) as f:\n                    content = f.read()\n            else:\n                from distutils.spawn import find_executable\n                content = find_executable(script)\n    \n            # and let's check that it is our script\n>           out = runner.run([script, '--version'], protocol=StdOutErrCapture)\n\nr2e_tests/test_2.py:218: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \ndatalad/cmd.py:149: in run\n    results = run_command(\ndatalad/nonasyncrunner.py:194: in run_command\n    process = subprocess.Popen(cmd, **kwargs)\n../../../../.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/subprocess.py:951: in __init__\n    self._execute_child(args, executable, preexec_fn, close_fds,\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = <Popen: returncode: 255 args: ['datalad', '--version']>\nargs = ['datalad', '--version'], executable = b'datalad', preexec_fn = None\nclose_fds = True, pass_fds = (), cwd = None, env = None, startupinfo = None\ncreationflags = 0, shell = False, p2cread = -1, p2cwrite = -1, c2pread = 13\nc2pwrite = 15, errread = 16, errwrite = 17, restore_signals = True, gid = None\ngids = None, uid = None, umask = -1, start_new_session = False\n\n    def _execute_child(self, args, executable, preexec_fn, close_fds,\n                       pass_fds, cwd, env,\n                       startupinfo, creationflags, shell,\n                       p2cread, p2cwrite,\n                       c2pread, c2pwrite,\n                       errread, errwrite,\n                       restore_signals,\n                       gid, gids, uid, umask,\n                       start_new_session):\n        \"\"\"Execute program (POSIX version)\"\"\"\n    \n        if isinstance(args, (str, bytes)):\n            args = [args]\n        elif isinstance(args, os.PathLike):\n            if shell:\n                raise TypeError('path-like args is not allowed when '\n                                'shell is true')\n            args = [args]\n        else:\n            args = list(args)\n    \n        if shell:\n            # On Android the default shell is at '/system/bin/sh'.\n            unix_shell = ('/system/bin/sh' if\n                      hasattr(sys, 'getandroidapilevel') else '/bin/sh')\n            args = [unix_shell, \"-c\"] + args\n            if executable:\n                args[0] = executable\n    \n        if executable is None:\n            executable = args[0]\n    \n        sys.audit(\"subprocess.Popen\", executable, args, cwd, env)\n    \n        if (_USE_POSIX_SPAWN\n                and os.path.dirname(executable)\n                and preexec_fn is None\n                and not close_fds\n                and not pass_fds\n                and cwd is None\n                and (p2cread == -1 or p2cread > 2)\n                and (c2pwrite == -1 or c2pwrite > 2)\n                and (errwrite == -1 or errwrite > 2)\n                and not start_new_session\n                and gid is None\n                and gids is None\n                and uid is None\n                and umask < 0):\n            self._posix_spawn(args, executable, env, restore_signals,\n                              p2cread, p2cwrite,\n                              c2pread, c2pwrite,\n                              errread, errwrite)\n            return\n    \n        orig_executable = executable\n    \n        # For transferring possible exec failure from child to parent.\n        # Data format: \"exception name:hex errno:description\"\n        # Pickle is not used; it is complex and involves memory allocation.\n        errpipe_read, errpipe_write = os.pipe()\n        # errpipe_write must not be in the standard io 0, 1, or 2 fd range.\n        low_fds_to_close = []\n        while errpipe_write < 3:\n            low_fds_to_close.append(errpipe_write)\n            errpipe_write = os.dup(errpipe_write)\n        for low_fd in low_fds_to_close:\n            os.close(low_fd)\n        try:\n            try:\n                # We must avoid complex work that could involve\n                # malloc or free in the child process to avoid\n                # potential deadlocks, thus we do all this here.\n                # and pass it to fork_exec()\n    \n                if env is not None:\n                    env_list = []\n                    for k, v in env.items():\n                        k = os.fsencode(k)\n                        if b'=' in k:\n                            raise ValueError(\"illegal environment variable name\")\n                        env_list.append(k + b'=' + os.fsencode(v))\n                else:\n                    env_list = None  # Use execv instead of execve.\n                executable = os.fsencode(executable)\n                if os.path.dirname(executable):\n                    executable_list = (executable,)\n                else:\n                    # This matches the behavior of os._execvpe().\n                    executable_list = tuple(\n                        os.path.join(os.fsencode(dir), executable)\n                        for dir in os.get_exec_path(env))\n                fds_to_keep = set(pass_fds)\n                fds_to_keep.add(errpipe_write)\n                self.pid = _posixsubprocess.fork_exec(\n                        args, executable_list,\n                        close_fds, tuple(sorted(map(int, fds_to_keep))),\n                        cwd, env_list,\n                        p2cread, p2cwrite, c2pread, c2pwrite,\n                        errread, errwrite,\n                        errpipe_read, errpipe_write,\n                        restore_signals, start_new_session,\n                        gid, gids, uid, umask,\n                        preexec_fn)\n                self._child_created = True\n            finally:\n                # be sure the FD is closed no matter what\n                os.close(errpipe_write)\n    \n            self._close_pipe_fds(p2cread, p2cwrite,\n                                 c2pread, c2pwrite,\n                                 errread, errwrite)\n    \n            # Wait for exec to fail or succeed; possibly raising an\n            # exception (limited in size)\n            errpipe_data = bytearray()\n            while True:\n                part = os.read(errpipe_read, 50000)\n                errpipe_data += part\n                if not part or len(errpipe_data) > 50000:\n                    break\n        finally:\n            # be sure the FD is closed no matter what\n            os.close(errpipe_read)\n    \n        if errpipe_data:\n            try:\n                pid, sts = os.waitpid(self.pid, 0)\n                if pid == self.pid:\n                    self._handle_exitstatus(sts)\n                else:\n                    self.returncode = sys.maxsize\n            except ChildProcessError:\n                pass\n    \n            try:\n                exception_name, hex_errno, err_msg = (\n                        errpipe_data.split(b':', 2))\n                # The encoding here should match the encoding\n                # written in by the subprocess implementations\n                # like _posixsubprocess\n                err_msg = err_msg.decode()\n            except ValueError:\n                exception_name = b'SubprocessError'\n                hex_errno = b'0'\n                err_msg = 'Bad exception data from child: {!r}'.format(\n                              bytes(errpipe_data))\n            child_exception_type = getattr(\n                    builtins, exception_name.decode('ascii'),\n                    SubprocessError)\n            if issubclass(child_exception_type, OSError) and hex_errno:\n                errno_num = int(hex_errno, 16)\n                child_exec_never_called = (err_msg == \"noexec\")\n                if child_exec_never_called:\n                    err_msg = \"\"\n                    # The error must be from chdir(cwd).\n                    err_filename = cwd\n                else:\n                    err_filename = orig_executable\n                if errno_num != 0:\n                    err_msg = os.strerror(errno_num)\n>               raise child_exception_type(errno_num, err_msg, err_filename)\nE               FileNotFoundError: [Errno 2] No such file or directory: 'datalad'\n\n../../../../.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/subprocess.py:1837: FileNotFoundError\n______________________________ test_cfg_override _______________________________\n\narg = (), kw = {'path': '/tmp/tmp-page2annexcuau0oxx'}\nfilename = '/tmp/datalad_temp_test_cfg_overrideb3ndu_af'\n\n    @wraps(t)\n    def  _wrap_with_tempfile(*arg, **kw):\n        if 'dir' not in tkwargs.keys():\n            # if not specified otherwise, respect datalad.tests.temp.dir config\n            # as this is a test helper\n            tkwargs['dir'] = dl_cfg.get(\"datalad.tests.temp.dir\")\n        with make_tempfile(wrapped=t, **tkwargs) as filename:\n>           return t(*(arg + (filename,)), **kw)\nE           TypeError: test_cfg_override() got multiple values for argument 'path'\n\ndatalad/tests/utils.py:739: TypeError\n==================================== PASSES ====================================\n_______________________ test_usage_on_insufficient_args ________________________\n----------------------------- Captured stderr call -----------------------------\n[ERROR] Please provide at least a source or a path [install.py:__call__:183] (InsufficientArgumentsError) \n------------------------------ Captured log call -------------------------------\nERROR    datalad.cmdline:main.py:219 Please provide at least a source or a path [install.py:__call__:183] (InsufficientArgumentsError)\n______________________ test_subcmd_usage_on_unknown_args _______________________\n----------------------------- Captured stderr call -----------------------------\n[ERROR] unknown argument: --murks \n------------------------------ Captured log call -------------------------------\nERROR    datalad.cmdline:main.py:172 unknown argument: --murks\n_________________________ test_incorrect_cfg_override __________________________\n----------------------------- Captured stderr call -----------------------------\n[ERROR] Configuration override without section/variable or value assignment (must be 'section.variable=value'): ['some'] \n[ERROR] Configuration override without section/variable or value assignment (must be 'section.variable=value'): ['some='] \n[ERROR] Configuration override without section/variable or value assignment (must be 'section.variable=value'): ['some.var'] \n[ERROR] Configuration override without section/variable or value assignment (must be 'section.variable=value'): ['some.var='] \n------------------------------ Captured log call -------------------------------\nERROR    datalad.cmdline:helpers.py:662 Configuration override without section/variable or value assignment (must be 'section.variable=value'): ['some']\nERROR    datalad.cmdline:helpers.py:662 Configuration override without section/variable or value assignment (must be 'section.variable=value'): ['some=']\nERROR    datalad.cmdline:helpers.py:662 Configuration override without section/variable or value assignment (must be 'section.variable=value'): ['some.var']\nERROR    datalad.cmdline:helpers.py:662 Configuration override without section/variable or value assignment (must be 'section.variable=value'): ['some.var=']\n_____________________________ test_fix_datalad_ri ______________________________\n----------------------------- Captured stderr call -----------------------------\n[INFO] Changing // back to /// as it was probably changed by MINGW/MSYS, see http://www.mingw.org/wiki/Posix_path_conversion \n[INFO] Changing //a back to ///a as it was probably changed by MINGW/MSYS, see http://www.mingw.org/wiki/Posix_path_conversion \n[INFO] Changing //a/b back to ///a/b as it was probably changed by MINGW/MSYS, see http://www.mingw.org/wiki/Posix_path_conversion \n------------------------------ Captured log call -------------------------------\nINFO     datalad.cmdline:helpers.py:614 Changing // back to /// as it was probably changed by MINGW/MSYS, see http://www.mingw.org/wiki/Posix_path_conversion\nINFO     datalad.cmdline:helpers.py:614 Changing //a back to ///a as it was probably changed by MINGW/MSYS, see http://www.mingw.org/wiki/Posix_path_conversion\nINFO     datalad.cmdline:helpers.py:614 Changing //a/b back to ///a/b as it was probably changed by MINGW/MSYS, see http://www.mingw.org/wiki/Posix_path_conversion\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::test_verify_ria_url\nPASSED r2e_tests/test_2.py::test_version\nPASSED r2e_tests/test_2.py::test_usage_on_insufficient_args\nPASSED r2e_tests/test_2.py::test_subcmd_usage_on_unknown_args\nPASSED r2e_tests/test_2.py::test_combined_short_option\nPASSED r2e_tests/test_2.py::test_incorrect_cfg_override\nPASSED r2e_tests/test_2.py::test_fail_with_short_help\nPASSED r2e_tests/test_2.py::test_fix_datalad_ri\nSKIPPED [1] .venv/lib/python3.9/site-packages/_pytest/unittest.py:385: Could not determine terminal size, skipping the rest of the test\nXFAIL r2e_tests/test_1.py::test_setup_store - reason: [NOTRUN] yield tests were removed in pytest 4.0 - test_setup_store will be ignored\nXFAIL r2e_tests/test_1.py::test_setup_ds_in_store - reason: [NOTRUN] yield tests were removed in pytest 4.0 - test_setup_ds_in_store will be ignored\nXFAIL r2e_tests/test_2.py::test_incorrect_options - reason: [NOTRUN] yield tests were removed in pytest 4.0 - test_incorrect_options will be ignored\nERROR r2e_tests/test_2.py::test_commanderror_jsonmsgs\nFAILED r2e_tests/test_2.py::test_script_shims - FileNotFoundError: [Errno 2] ...\nFAILED r2e_tests/test_2.py::test_cfg_override - TypeError: test_cfg_override(...\n========== 2 failed, 8 passed, 1 skipped, 3 xfailed, 1 error in 5.12s ==========\n",
    "new_commit_res_stderr": "",
    "old_commit_res_code": 1,
    "old_commit_res_stdout": "============================= test session starts ==============================\nplatform linux -- Python 3.9.21, pytest-8.3.4, pluggy-1.5.0\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_22948af54d5469e5c708414a262e968ecbf593d9\nconfigfile: pyproject.toml\nplugins: cov-6.0.0\ncollected 15 items\n\nr2e_tests/test_1.py xxF                                                  [ 20%]\nr2e_tests/test_2.py .s...xFF...E                                         [100%]\n\n==================================== ERRORS ====================================\n_________________ ERROR at setup of test_commanderror_jsonmsgs _________________\nfile /home/gcpuser/buckets/local_repoeval_bucket/repos/datalad_22948af54d5469e5c708414a262e968ecbf593d9/r2e_tests/test_2.py, line 366\n  @with_tempfile\n  @with_tempfile(mkdir=True)\n  def test_commanderror_jsonmsgs(src, exp):\nE       fixture 'src' 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_22948af54d5469e5c708414a262e968ecbf593d9/r2e_tests/test_2.py:366\n=================================== FAILURES ===================================\n_____________________________ test_verify_ria_url ______________________________\n\n    def test_verify_ria_url():\n        # unsupported protocol\n        assert_raises(ValueError,\n                      verify_ria_url, 'ria+ftp://localhost/tmp/this', {})\n        # bunch of caes that should work\n        cases = {\n            'ria+file:///tmp/this': (None, '/tmp/this'),\n            # no normalization\n            'ria+file:///tmp/this/': (None, '/tmp/this/'),\n            # with hosts\n            'ria+ssh://localhost/tmp/this': ('ssh://localhost', '/tmp/this'),\n            'ria+http://localhost/tmp/this': ('http://localhost', '/tmp/this'),\n            'ria+https://localhost/tmp/this': ('https://localhost', '/tmp/this'),\n            # with username\n            'ria+ssh://humbug@localhost/tmp/this':\n                ('ssh://humbug@localhost', '/tmp/this'),\n            # with port\n            'ria+ssh://humbug@localhost:2222/tmp/this':\n                ('ssh://humbug@localhost:2222', '/tmp/this'),\n            'ria+ssh://localhost:2200/tmp/this':\n                ('ssh://localhost:2200', '/tmp/this'),\n            # with password\n            'ria+https://humbug:1234@localhost:8080/tmp/this':\n                ('https://humbug:1234@localhost:8080', '/tmp/this'),\n            # document a strange (MIH thinks undesirable), but pre-existing\n            # behavior an 'ssh example.com' would end up in the user HOME,\n            # not in '/'\n            'ria+ssh://example.com': ('ssh://example.com', '/')\n        }\n        for i, o in cases.items():\n            # we are not testing the URL rewriting here\n>           assert_equal(o, verify_ria_url(i, {})[:2])\n\nr2e_tests/test_1.py:159: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n../../../../.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py:837: in assertEqual\n    assertion_func(first, second, msg=msg)\n../../../../.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py:1054: in assertTupleEqual\n    self.assertSequenceEqual(tuple1, tuple2, msg, seq_type=tuple)\n../../../../.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py:1025: in assertSequenceEqual\n    self.fail(msg)\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = <nose.tools.trivial.Dummy testMethod=nop>\nmsg = \"Tuples differ: ('ssh://localhost', '/tmp/this') != ('localhost', '/tmp/this')\\n\\nFirst differing element 0:\\n'ssh://localhost'\\n'localhost'\\n\\n- ('ssh://localhost', '/tmp/this')\\n?   ------\\n\\n+ ('localhost', '/tmp/this')\"\n\n    def fail(self, msg=None):\n        \"\"\"Fail immediately, with the given message.\"\"\"\n>       raise self.failureException(msg)\nE       AssertionError: Tuples differ: ('ssh://localhost', '/tmp/this') != ('localhost', '/tmp/this')\nE       \nE       First differing element 0:\nE       'ssh://localhost'\nE       'localhost'\nE       \nE       - ('ssh://localhost', '/tmp/this')\nE       ?   ------\nE       \nE       + ('localhost', '/tmp/this')\n\n../../../../.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/unittest/case.py:676: AssertionError\n______________________________ test_script_shims _______________________________\n\n    def test_script_shims():\n        runner = Runner()\n        for script in [\n            'datalad',\n            'git-annex-remote-datalad-archives',\n            'git-annex-remote-datalad']:\n            if not on_windows:\n                # those must be available for execution, and should not contain\n                which = runner.run(['which', script], protocol=StdOutErrCapture)['stdout']\n                # test if there is no easy install shim in there\n                with open(which.rstrip()) as f:\n                    content = f.read()\n            else:\n                from distutils.spawn import find_executable\n                content = find_executable(script)\n    \n            # and let's check that it is our script\n>           out = runner.run([script, '--version'], protocol=StdOutErrCapture)\n\nr2e_tests/test_2.py:218: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \ndatalad/cmd.py:149: in run\n    results = run_command(\ndatalad/nonasyncrunner.py:194: in run_command\n    process = subprocess.Popen(cmd, **kwargs)\n../../../../.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/subprocess.py:951: in __init__\n    self._execute_child(args, executable, preexec_fn, close_fds,\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = <Popen: returncode: 255 args: ['datalad', '--version']>\nargs = ['datalad', '--version'], executable = b'datalad', preexec_fn = None\nclose_fds = True, pass_fds = (), cwd = None, env = None, startupinfo = None\ncreationflags = 0, shell = False, p2cread = -1, p2cwrite = -1, c2pread = 13\nc2pwrite = 15, errread = 16, errwrite = 17, restore_signals = True, gid = None\ngids = None, uid = None, umask = -1, start_new_session = False\n\n    def _execute_child(self, args, executable, preexec_fn, close_fds,\n                       pass_fds, cwd, env,\n                       startupinfo, creationflags, shell,\n                       p2cread, p2cwrite,\n                       c2pread, c2pwrite,\n                       errread, errwrite,\n                       restore_signals,\n                       gid, gids, uid, umask,\n                       start_new_session):\n        \"\"\"Execute program (POSIX version)\"\"\"\n    \n        if isinstance(args, (str, bytes)):\n            args = [args]\n        elif isinstance(args, os.PathLike):\n            if shell:\n                raise TypeError('path-like args is not allowed when '\n                                'shell is true')\n            args = [args]\n        else:\n            args = list(args)\n    \n        if shell:\n            # On Android the default shell is at '/system/bin/sh'.\n            unix_shell = ('/system/bin/sh' if\n                      hasattr(sys, 'getandroidapilevel') else '/bin/sh')\n            args = [unix_shell, \"-c\"] + args\n            if executable:\n                args[0] = executable\n    \n        if executable is None:\n            executable = args[0]\n    \n        sys.audit(\"subprocess.Popen\", executable, args, cwd, env)\n    \n        if (_USE_POSIX_SPAWN\n                and os.path.dirname(executable)\n                and preexec_fn is None\n                and not close_fds\n                and not pass_fds\n                and cwd is None\n                and (p2cread == -1 or p2cread > 2)\n                and (c2pwrite == -1 or c2pwrite > 2)\n                and (errwrite == -1 or errwrite > 2)\n                and not start_new_session\n                and gid is None\n                and gids is None\n                and uid is None\n                and umask < 0):\n            self._posix_spawn(args, executable, env, restore_signals,\n                              p2cread, p2cwrite,\n                              c2pread, c2pwrite,\n                              errread, errwrite)\n            return\n    \n        orig_executable = executable\n    \n        # For transferring possible exec failure from child to parent.\n        # Data format: \"exception name:hex errno:description\"\n        # Pickle is not used; it is complex and involves memory allocation.\n        errpipe_read, errpipe_write = os.pipe()\n        # errpipe_write must not be in the standard io 0, 1, or 2 fd range.\n        low_fds_to_close = []\n        while errpipe_write < 3:\n            low_fds_to_close.append(errpipe_write)\n            errpipe_write = os.dup(errpipe_write)\n        for low_fd in low_fds_to_close:\n            os.close(low_fd)\n        try:\n            try:\n                # We must avoid complex work that could involve\n                # malloc or free in the child process to avoid\n                # potential deadlocks, thus we do all this here.\n                # and pass it to fork_exec()\n    \n                if env is not None:\n                    env_list = []\n                    for k, v in env.items():\n                        k = os.fsencode(k)\n                        if b'=' in k:\n                            raise ValueError(\"illegal environment variable name\")\n                        env_list.append(k + b'=' + os.fsencode(v))\n                else:\n                    env_list = None  # Use execv instead of execve.\n                executable = os.fsencode(executable)\n                if os.path.dirname(executable):\n                    executable_list = (executable,)\n                else:\n                    # This matches the behavior of os._execvpe().\n                    executable_list = tuple(\n                        os.path.join(os.fsencode(dir), executable)\n                        for dir in os.get_exec_path(env))\n                fds_to_keep = set(pass_fds)\n                fds_to_keep.add(errpipe_write)\n                self.pid = _posixsubprocess.fork_exec(\n                        args, executable_list,\n                        close_fds, tuple(sorted(map(int, fds_to_keep))),\n                        cwd, env_list,\n                        p2cread, p2cwrite, c2pread, c2pwrite,\n                        errread, errwrite,\n                        errpipe_read, errpipe_write,\n                        restore_signals, start_new_session,\n                        gid, gids, uid, umask,\n                        preexec_fn)\n                self._child_created = True\n            finally:\n                # be sure the FD is closed no matter what\n                os.close(errpipe_write)\n    \n            self._close_pipe_fds(p2cread, p2cwrite,\n                                 c2pread, c2pwrite,\n                                 errread, errwrite)\n    \n            # Wait for exec to fail or succeed; possibly raising an\n            # exception (limited in size)\n            errpipe_data = bytearray()\n            while True:\n                part = os.read(errpipe_read, 50000)\n                errpipe_data += part\n                if not part or len(errpipe_data) > 50000:\n                    break\n        finally:\n            # be sure the FD is closed no matter what\n            os.close(errpipe_read)\n    \n        if errpipe_data:\n            try:\n                pid, sts = os.waitpid(self.pid, 0)\n                if pid == self.pid:\n                    self._handle_exitstatus(sts)\n                else:\n                    self.returncode = sys.maxsize\n            except ChildProcessError:\n                pass\n    \n            try:\n                exception_name, hex_errno, err_msg = (\n                        errpipe_data.split(b':', 2))\n                # The encoding here should match the encoding\n                # written in by the subprocess implementations\n                # like _posixsubprocess\n                err_msg = err_msg.decode()\n            except ValueError:\n                exception_name = b'SubprocessError'\n                hex_errno = b'0'\n                err_msg = 'Bad exception data from child: {!r}'.format(\n                              bytes(errpipe_data))\n            child_exception_type = getattr(\n                    builtins, exception_name.decode('ascii'),\n                    SubprocessError)\n            if issubclass(child_exception_type, OSError) and hex_errno:\n                errno_num = int(hex_errno, 16)\n                child_exec_never_called = (err_msg == \"noexec\")\n                if child_exec_never_called:\n                    err_msg = \"\"\n                    # The error must be from chdir(cwd).\n                    err_filename = cwd\n                else:\n                    err_filename = orig_executable\n                if errno_num != 0:\n                    err_msg = os.strerror(errno_num)\n>               raise child_exception_type(errno_num, err_msg, err_filename)\nE               FileNotFoundError: [Errno 2] No such file or directory: 'datalad'\n\n../../../../.local/share/uv/python/cpython-3.9.21-linux-x86_64-gnu/lib/python3.9/subprocess.py:1837: FileNotFoundError\n______________________________ test_cfg_override _______________________________\n\narg = (), kw = {'path': '/tmp/tmp-page2annexbjhpg9zo'}\nfilename = '/tmp/datalad_temp_test_cfg_overridehpu8ynd9'\n\n    @wraps(t)\n    def  _wrap_with_tempfile(*arg, **kw):\n        if 'dir' not in tkwargs.keys():\n            # if not specified otherwise, respect datalad.tests.temp.dir config\n            # as this is a test helper\n            tkwargs['dir'] = dl_cfg.get(\"datalad.tests.temp.dir\")\n        with make_tempfile(wrapped=t, **tkwargs) as filename:\n>           return t(*(arg + (filename,)), **kw)\nE           TypeError: test_cfg_override() got multiple values for argument 'path'\n\ndatalad/tests/utils.py:739: TypeError\n==================================== PASSES ====================================\n_______________________ test_usage_on_insufficient_args ________________________\n----------------------------- Captured stderr call -----------------------------\n[ERROR] Please provide at least a source or a path [install.py:__call__:183] (InsufficientArgumentsError) \n------------------------------ Captured log call -------------------------------\nERROR    datalad.cmdline:main.py:219 Please provide at least a source or a path [install.py:__call__:183] (InsufficientArgumentsError)\n______________________ test_subcmd_usage_on_unknown_args _______________________\n----------------------------- Captured stderr call -----------------------------\n[ERROR] unknown argument: --murks \n------------------------------ Captured log call -------------------------------\nERROR    datalad.cmdline:main.py:172 unknown argument: --murks\n_________________________ test_incorrect_cfg_override __________________________\n----------------------------- Captured stderr call -----------------------------\n[ERROR] Configuration override without section/variable or value assignment (must be 'section.variable=value'): ['some'] \n[ERROR] Configuration override without section/variable or value assignment (must be 'section.variable=value'): ['some='] \n[ERROR] Configuration override without section/variable or value assignment (must be 'section.variable=value'): ['some.var'] \n[ERROR] Configuration override without section/variable or value assignment (must be 'section.variable=value'): ['some.var='] \n------------------------------ Captured log call -------------------------------\nERROR    datalad.cmdline:helpers.py:662 Configuration override without section/variable or value assignment (must be 'section.variable=value'): ['some']\nERROR    datalad.cmdline:helpers.py:662 Configuration override without section/variable or value assignment (must be 'section.variable=value'): ['some=']\nERROR    datalad.cmdline:helpers.py:662 Configuration override without section/variable or value assignment (must be 'section.variable=value'): ['some.var']\nERROR    datalad.cmdline:helpers.py:662 Configuration override without section/variable or value assignment (must be 'section.variable=value'): ['some.var=']\n_____________________________ test_fix_datalad_ri ______________________________\n----------------------------- Captured stderr call -----------------------------\n[INFO] Changing // back to /// as it was probably changed by MINGW/MSYS, see http://www.mingw.org/wiki/Posix_path_conversion \n[INFO] Changing //a back to ///a as it was probably changed by MINGW/MSYS, see http://www.mingw.org/wiki/Posix_path_conversion \n[INFO] Changing //a/b back to ///a/b as it was probably changed by MINGW/MSYS, see http://www.mingw.org/wiki/Posix_path_conversion \n------------------------------ Captured log call -------------------------------\nINFO     datalad.cmdline:helpers.py:614 Changing // back to /// as it was probably changed by MINGW/MSYS, see http://www.mingw.org/wiki/Posix_path_conversion\nINFO     datalad.cmdline:helpers.py:614 Changing //a back to ///a as it was probably changed by MINGW/MSYS, see http://www.mingw.org/wiki/Posix_path_conversion\nINFO     datalad.cmdline:helpers.py:614 Changing //a/b back to ///a/b as it was probably changed by MINGW/MSYS, see http://www.mingw.org/wiki/Posix_path_conversion\n=========================== short test summary info ============================\nPASSED r2e_tests/test_2.py::test_version\nPASSED r2e_tests/test_2.py::test_usage_on_insufficient_args\nPASSED r2e_tests/test_2.py::test_subcmd_usage_on_unknown_args\nPASSED r2e_tests/test_2.py::test_combined_short_option\nPASSED r2e_tests/test_2.py::test_incorrect_cfg_override\nPASSED r2e_tests/test_2.py::test_fail_with_short_help\nPASSED r2e_tests/test_2.py::test_fix_datalad_ri\nSKIPPED [1] .venv/lib/python3.9/site-packages/_pytest/unittest.py:385: Could not determine terminal size, skipping the rest of the test\nXFAIL r2e_tests/test_1.py::test_setup_store - reason: [NOTRUN] yield tests were removed in pytest 4.0 - test_setup_store will be ignored\nXFAIL r2e_tests/test_1.py::test_setup_ds_in_store - reason: [NOTRUN] yield tests were removed in pytest 4.0 - test_setup_ds_in_store will be ignored\nXFAIL r2e_tests/test_2.py::test_incorrect_options - reason: [NOTRUN] yield tests were removed in pytest 4.0 - test_incorrect_options will be ignored\nERROR r2e_tests/test_2.py::test_commanderror_jsonmsgs\nFAILED r2e_tests/test_1.py::test_verify_ria_url - AssertionError: Tuples diff...\nFAILED r2e_tests/test_2.py::test_script_shims - FileNotFoundError: [Errno 2] ...\nFAILED r2e_tests/test_2.py::test_cfg_override - TypeError: test_cfg_override(...\n========== 3 failed, 7 passed, 1 skipped, 3 xfailed, 1 error in 1.57s ==========\n",
    "old_commit_res_stderr": ""
}
datalad/cmdline/tests/test_main.py datalad/customremotes/ria_utils.py datalad/customremotes/tests/test_ria_utils.py
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** `verify_ria_url` Returns Incomplete Host Without Protocol and Credentials

**Description:**
The `verify_ria_url` function is designed to parse RIA URLs and return a tuple containing the host and path. However, when dealing with URLs that include protocols other than `file`, such as `ssh` or `http`, the function currently returns only the hostname without including the protocol, username, password, or port information. This incomplete host information leads to inconsistencies and causes related tests to fail.

**Buggy Code Example:**
```python
from datalad.customremotes.ria_utils import verify_ria_url

url = 'ria+ssh://humbug@localhost:2222/tmp/this'
host, path = verify_ria_url(url, {})
print(host)  # Outputs 'localhost' instead of 'ssh://humbug@localhost:2222'
```

**Expected Behavior:**
When parsing a RIA URL with a non-file protocol that includes credentials and a port, the `host` returned by `verify_ria_url` should encompass the full information, including the protocol, username, password, and port. For the example above, the expected output is:
```
'ssh://humbug@localhost:2222'
```

**Actual Behavior:**
The function currently returns only the hostname without the protocol and credentials:
```
'localhost'
```

This omission results in failed assertions in tests that expect the complete host information, leading to confusion and difficulty in handling URLs correctly.

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

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

--- BEGIN FILE ---
```
### datalad/customremotes/ria_utils.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.
#
# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
"""Helper for RIA stores

"""

import logging
from pathlib import Path


lgr = logging.getLogger('datalad.customremotes.ria_utils')


class UnknownLayoutVersion(Exception):
    pass


# TODO: Make versions a tuple of (label, description)?
# Object tree versions we introduced so far. This is about the layout within a
# dataset in a RIA store
known_versions_objt = ['1', '2']
# Dataset tree versions we introduced so far. This is about the layout of
# datasets in a RIA store
known_versions_dst = ['1']


# TODO: This is wrong and should consider both versions (store+dataset)
def get_layout_locations(version, base_path, dsid):
    """Return dataset-related path in a RIA store

    Parameters
    ----------
    version : int
      Layout version of the store.
    base_path : Path
      Base path of the store.
    dsid : str
      Dataset ID

    Returns
    -------
    Path, Path, Path
      The location of the bare dataset repository in the store,
      the directory with archive files for the dataset, and the
      annex object directory are return in that order.
    """
    if version == 1:
        dsgit_dir = base_path / dsid[:3] / dsid[3:]
        archive_dir = dsgit_dir / 'archives'
        dsobj_dir = dsgit_dir / 'annex' / 'objects'
        return dsgit_dir, archive_dir, dsobj_dir
    else:
        raise ValueError("Unknown layout version: {}. Supported: {}"
                         "".format(version, known_versions_dst))


def verify_ria_url(url, cfg):
    """Verify and decode ria url

    Expects a ria-URL pointing to a RIA store, applies rewrites and tries to
    decode potential host and base path for the store from it. Additionally
    raises if `url` is considered invalid.

    ria+ssh://somehost:/path/to/store
    ria+file:///path/to/store

    Parameters
    ----------
    url : str
      URL to verify an decode.
    cfg : dict-like
      Configuration settings for rewrite_url()

    Raises
    ------
    ValueError

    Returns
    -------
    tuple
      (host, base-path, rewritten url)
    """
    from datalad.config import rewrite_url
    from datalad.support.network import URL

    if not url:
        raise ValueError("Got no URL")

    url = rewrite_url(cfg, url)
    url_ri = URL(url)
    if not url_ri.scheme.startswith('ria+'):
        raise ValueError("Missing ria+ prefix in final URL: %s" % url)
    if url_ri.fragment:
        raise ValueError(
            "Unexpected fragment in RIA-store URL: %s" % url_ri.fragment)
    protocol = url_ri.scheme[4:]
    if protocol not in ['ssh', 'file', 'http', 'https']:
        raise ValueError("Unsupported protocol: %s. "
                         "Supported: ssh, file, http(s)" %
                         protocol)

    return url_ri.hostname if protocol != 'file' else None, \
        url_ri.path if url_ri.path else '/', \
        url


def _ensure_version(io, base_path, version):
    """Check a store or dataset version and make sure it is declared

    Parameters
    ----------
    io: SSHRemoteIO or LocalIO
    base_path: Path
      root path of a store or dataset
    version: str
      target layout version of the store (dataset tree)
    """
    version_file = base_path / 'ria-layout-version'
    if io.exists(version_file):
        existing_version = io.read_file(version_file).split('|')[0].strip()
        if existing_version != version.split('|')[0]:
            # We have an already existing location with a conflicting version on
            # record.
            # Note, that a config flag after pipe symbol is fine.
            raise ValueError("Conflicting version found at target: {}"
                             .format(existing_version))
        else:
            # already exists, recorded version fits - nothing to do
            return
    # Note, that the following does create the base-path dir as well, since
    # mkdir has parents=True:
    io.mkdir(base_path)
    io.write_file(version_file, version)


def create_store(io, base_path, version):
    """Helper to create a RIA store

    Note, that this is meant as an internal helper and part of intermediate
    RF'ing. Ultimately should lead to dedicated command or option for
    create-sibling-ria.

    Parameters
    ----------
    io: SSHRemoteIO or LocalIO
      Respective execution instance.
      Note: To be replaced by proper command abstraction
    base_path: Path
      root path of the store
    version: str
      layout version of the store (dataset tree)
    """

    # At store level the only version we know as of now is 1.
    if version not in known_versions_dst:
        raise UnknownLayoutVersion("RIA store layout version unknown: {}."
                                   "Supported versions: {}"
                                   .format(version, known_versions_dst))
    _ensure_version(io, base_path, version)
    error_logs = base_path / 'error_logs'
    io.mkdir(error_logs)


def create_ds_in_store(io, base_path, dsid, obj_version, store_version, alias=None):
    """Helper to create a dataset in a RIA store

    Note, that this is meant as an internal helper and part of intermediate
    RF'ing. Ultimately should lead to a version option for create-sibling-ria
    in conjunction with a store creation command/option.

    Parameters
    ----------
    io: SSHRemoteIO or LocalIO
      Respective execution instance.
      Note: To be replaced by proper command abstraction
    base_path: Path
      root path of the store
    dsid: str
      dataset id
    store_version: str
      layout version of the store (dataset tree)
    obj_version: str
      layout version of the dataset itself (object tree)
    alias: str, optional
      alias for the dataset in the store
    """

    # TODO: Note for RF'ing, that this is about setting up a valid target
    #       for the special remote not a replacement for create-sibling-ria.
    #       There's currently no git (bare) repo created.

    try:
        # TODO: This is currently store layout version!
        #       Too entangled by current get_layout_locations.
        dsgit_dir, archive_dir, dsobj_dir = \
            get_layout_locations(int(store_version), base_path, dsid)
    except ValueError as e:
        raise UnknownLayoutVersion(str(e))

    if obj_version not in known_versions_objt:
        raise UnknownLayoutVersion("Dataset layout version unknown: {}. "
                                   "Supported: {}"
                                   .format(obj_version, known_versions_objt))

    _ensure_version(io, dsgit_dir, obj_version)

    io.mkdir(archive_dir)
    io.mkdir(dsobj_dir)
    if alias:
        alias_dir = base_path / "alias"
        io.mkdir(alias_dir)
        try:
            # go for a relative path to keep the alias links valid
            # when moving a store
            io.symlink(
                Path('..') / dsgit_dir.relative_to(base_path),
                alias_dir / alias)
        except FileExistsError:
            lgr.warning("Alias %r already exists in the RIA store, not adding an "
                        "alias.", alias)

```
--- 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:** `verify_ria_url` Returns Incomplete Host Without Protocol and Credentials

**Description:**
The `verify_ria_url` function is designed to parse RIA URLs and return a tuple containing the host and path. However, when dealing with URLs that include protocols other than `file`, such as `ssh` or `http`, the function currently returns only the hostname without including the protocol, username, password, or port information. This incomplete host information leads to inconsistencies and causes related tests to fail.

**Buggy Code Example:**
```python
from datalad.customremotes.ria_utils import verify_ria_url

url = 'ria+ssh://humbug@localhost:2222/tmp/this'
host, path = verify_ria_url(url, {})
print(host)  # Outputs 'localhost' instead of 'ssh://humbug@localhost:2222'
```

**Expected Behavior:**
When parsing a RIA URL with a non-file protocol that includes credentials and a port, the `host` returned by `verify_ria_url` should encompass the full information, including the protocol, username, password, and port. For the example above, the expected output is:
```
'ssh://humbug@localhost:2222'
```

**Actual Behavior:**
The function currently returns only the hostname without the protocol and credentials:
```
'localhost'
```

This omission results in failed assertions in tests that expect the complete host information, leading to confusion and difficulty in handling URLs correctly.

[/ISSUE]
{
    "test_verify_ria_url": "PASSED",
    "test_version": "PASSED",
    "test_usage_on_insufficient_args": "PASSED",
    "test_subcmd_usage_on_unknown_args": "PASSED",
    "test_combined_short_option": "PASSED",
    "test_script_shims": "PASSED",
    "test_incorrect_cfg_override": "PASSED",
    "test_fail_with_short_help": "PASSED",
    "test_fix_datalad_ri": "PASSED",
    "test_commanderror_jsonmsgs": "ERROR",
    "test_cfg_override": "FAILED"
}
diff --git a/datalad/customremotes/ria_utils.py b/datalad/customremotes/ria_utils.py
index e3cf01604..1e8f59ca6 100644
--- a/datalad/customremotes/ria_utils.py
+++ b/datalad/customremotes/ria_utils.py
@@ -85,6 +85,8 @@ def verify_ria_url(url, cfg):
     -------
     tuple
       (host, base-path, rewritten url)
+      `host` is not just a hostname, but is a stub URL that may also contain
+      username, password, and port, if specified in a given URL.
     """
     from datalad.config import rewrite_url
     from datalad.support.network import URL
@@ -105,7 +107,17 @@ def verify_ria_url(url, cfg):
                          "Supported: ssh, file, http(s)" %
                          protocol)
 
-    return url_ri.hostname if protocol != 'file' else None, \
+    host = '{proto}://{user}{pdlm}{passwd}{udlm}{host}{portdlm}{port}'.format(
+        proto=protocol,
+        user=url_ri.username or '',
+        pdlm=':' if url_ri.password else '',
+        passwd=url_ri.password or '',
+        udlm='@' if url_ri.username else '',
+        host=url_ri.hostname or '',
+        portdlm=':' if url_ri.port else '',
+        port=url_ri.port or '',
+    )
+    return host if protocol != 'file' else None, \
         url_ri.path if url_ri.path else '/', \
         url
 

{"datalad/customremotes/ria_utils.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\"\"\"Helper for RIA stores\n\n\"\"\"\n\nimport logging\nfrom pathlib import Path\n\n\nlgr = logging.getLogger('datalad.customremotes.ria_utils')\n\n\nclass UnknownLayoutVersion(Exception):\n    pass\n\n\n# TODO: Make versions a tuple of (label, description)?\n# Object tree versions we introduced so far. This is about the layout within a\n# dataset in a RIA store\nknown_versions_objt = ['1', '2']\n# Dataset tree versions we introduced so far. This is about the layout of\n# datasets in a RIA store\nknown_versions_dst = ['1']\n\n\n# TODO: This is wrong and should consider both versions (store+dataset)\ndef get_layout_locations(version, base_path, dsid):\n    \"\"\"Return dataset-related path in a RIA store\n\n    Parameters\n    ----------\n    version : int\n      Layout version of the store.\n    base_path : Path\n      Base path of the store.\n    dsid : str\n      Dataset ID\n\n    Returns\n    -------\n    Path, Path, Path\n      The location of the bare dataset repository in the store,\n      the directory with archive files for the dataset, and the\n      annex object directory are return in that order.\n    \"\"\"\n    if version == 1:\n        dsgit_dir = base_path / dsid[:3] / dsid[3:]\n        archive_dir = dsgit_dir / 'archives'\n        dsobj_dir = dsgit_dir / 'annex' / 'objects'\n        return dsgit_dir, archive_dir, dsobj_dir\n    else:\n        raise ValueError(\"Unknown layout version: {}. Supported: {}\"\n                         \"\".format(version, known_versions_dst))\n\n\ndef verify_ria_url(url, cfg):\n    \"\"\"Verify and decode ria url\n\n    Expects a ria-URL pointing to a RIA store, applies rewrites and tries to\n    decode potential host and base path for the store from it. Additionally\n    raises if `url` is considered invalid.\n\n    ria+ssh://somehost:/path/to/store\n    ria+file:///path/to/store\n\n    Parameters\n    ----------\n    url : str\n      URL to verify an decode.\n    cfg : dict-like\n      Configuration settings for rewrite_url()\n\n    Raises\n    ------\n    ValueError\n\n    Returns\n    -------\n    tuple\n      (host, base-path, rewritten url)\n    \"\"\"\n    from datalad.config import rewrite_url\n    from datalad.support.network import URL\n\n    if not url:\n        raise ValueError(\"Got no URL\")\n\n    url = rewrite_url(cfg, url)\n    url_ri = URL(url)\n    if not url_ri.scheme.startswith('ria+'):\n        raise ValueError(\"Missing ria+ prefix in final URL: %s\" % url)\n    if url_ri.fragment:\n        raise ValueError(\n            \"Unexpected fragment in RIA-store URL: %s\" % url_ri.fragment)\n    protocol = url_ri.scheme[4:]\n    if protocol not in ['ssh', 'file', 'http', 'https']:\n        raise ValueError(\"Unsupported protocol: %s. \"\n                         \"Supported: ssh, file, http(s)\" %\n                         protocol)\n\n    return url_ri.hostname if protocol != 'file' else None, \\\n        url_ri.path if url_ri.path else '/', \\\n        url\n\n\ndef _ensure_version(io, base_path, version):\n    \"\"\"Check a store or dataset version and make sure it is declared\n\n    Parameters\n    ----------\n    io: SSHRemoteIO or LocalIO\n    base_path: Path\n      root path of a store or dataset\n    version: str\n      target layout version of the store (dataset tree)\n    \"\"\"\n    version_file = base_path / 'ria-layout-version'\n    if io.exists(version_file):\n        existing_version = io.read_file(version_file).split('|')[0].strip()\n        if existing_version != version.split('|')[0]:\n            # We have an already existing location with a conflicting version on\n            # record.\n            # Note, that a config flag after pipe symbol is fine.\n            raise ValueError(\"Conflicting version found at target: {}\"\n                             .format(existing_version))\n        else:\n            # already exists, recorded version fits - nothing to do\n            return\n    # Note, that the following does create the base-path dir as well, since\n    # mkdir has parents=True:\n    io.mkdir(base_path)\n    io.write_file(version_file, version)\n\n\ndef create_store(io, base_path, version):\n    \"\"\"Helper to create a RIA store\n\n    Note, that this is meant as an internal helper and part of intermediate\n    RF'ing. Ultimately should lead to dedicated command or option for\n    create-sibling-ria.\n\n    Parameters\n    ----------\n    io: SSHRemoteIO or LocalIO\n      Respective execution instance.\n      Note: To be replaced by proper command abstraction\n    base_path: Path\n      root path of the store\n    version: str\n      layout version of the store (dataset tree)\n    \"\"\"\n\n    # At store level the only version we know as of now is 1.\n    if version not in known_versions_dst:\n        raise UnknownLayoutVersion(\"RIA store layout version unknown: {}.\"\n                                   \"Supported versions: {}\"\n                                   .format(version, known_versions_dst))\n    _ensure_version(io, base_path, version)\n    error_logs = base_path / 'error_logs'\n    io.mkdir(error_logs)\n\n\ndef create_ds_in_store(io, base_path, dsid, obj_version, store_version, alias=None):\n    \"\"\"Helper to create a dataset in a RIA store\n\n    Note, that this is meant as an internal helper and part of intermediate\n    RF'ing. Ultimately should lead to a version option for create-sibling-ria\n    in conjunction with a store creation command/option.\n\n    Parameters\n    ----------\n    io: SSHRemoteIO or LocalIO\n      Respective execution instance.\n      Note: To be replaced by proper command abstraction\n    base_path: Path\n      root path of the store\n    dsid: str\n      dataset id\n    store_version: str\n      layout version of the store (dataset tree)\n    obj_version: str\n      layout version of the dataset itself (object tree)\n    alias: str, optional\n      alias for the dataset in the store\n    \"\"\"\n\n    # TODO: Note for RF'ing, that this is about setting up a valid target\n    #       for the special remote not a replacement for create-sibling-ria.\n    #       There's currently no git (bare) repo created.\n\n    try:\n        # TODO: This is currently store layout version!\n        #       Too entangled by current get_layout_locations.\n        dsgit_dir, archive_dir, dsobj_dir = \\\n            get_layout_locations(int(store_version), base_path, dsid)\n    except ValueError as e:\n        raise UnknownLayoutVersion(str(e))\n\n    if obj_version not in known_versions_objt:\n        raise UnknownLayoutVersion(\"Dataset layout version unknown: {}. \"\n                                   \"Supported: {}\"\n                                   .format(obj_version, known_versions_objt))\n\n    _ensure_version(io, dsgit_dir, obj_version)\n\n    io.mkdir(archive_dir)\n    io.mkdir(dsobj_dir)\n    if alias:\n        alias_dir = base_path / \"alias\"\n        io.mkdir(alias_dir)\n        try:\n            # go for a relative path to keep the alias links valid\n            # when moving a store\n            io.symlink(\n                Path('..') / dsgit_dir.relative_to(base_path),\n                alias_dir / alias)\n        except FileExistsError:\n            lgr.warning(\"Alias %r already exists in the RIA store, not adding an \"\n                        \"alias.\", alias)\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:** `verify_ria_url` Returns Incomplete Host Without Protocol and Credentials

**Description:**
The `verify_ria_url` function is designed to parse RIA URLs and return a tuple containing the host and path. However, when dealing with URLs that include protocols other than `file`, such as `ssh` or `http`, the function currently returns only the hostname without including the protocol, username, password, or port information. This incomplete host information leads to inconsistencies and causes related tests to fail.

**Buggy Code Example:**
```python
from datalad.customremotes.ria_utils import verify_ria_url

url = 'ria+ssh://humbug@localhost:2222/tmp/this'
host, path = verify_ria_url(url, {})
print(host)  # Outputs 'localhost' instead of 'ssh://humbug@localhost:2222'
```

**Expected Behavior:**
When parsing a RIA URL with a non-file protocol that includes credentials and a port, the `host` returned by `verify_ria_url` should encompass the full information, including the protocol, username, password, and port. For the example above, the expected output is:
```
'ssh://humbug@localhost:2222'
```

**Actual Behavior:**
The function currently returns only the hostname without the protocol and credentials:
```
'localhost'
```

This omission results in failed assertions in tests that expect the complete host information, leading to confusion and difficulty in handling URLs correctly.

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

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

--- BEGIN FILE ---
```
### datalad/customremotes/ria_utils.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.
#
# ## ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
"""Helper for RIA stores

"""

import logging
from pathlib import Path


lgr = logging.getLogger('datalad.customremotes.ria_utils')


class UnknownLayoutVersion(Exception):
    pass


# TODO: Make versions a tuple of (label, description)?
# Object tree versions we introduced so far. This is about the layout within a
# dataset in a RIA store
known_versions_objt = ['1', '2']
# Dataset tree versions we introduced so far. This is about the layout of
# datasets in a RIA store
known_versions_dst = ['1']


# TODO: This is wrong and should consider both versions (store+dataset)
def get_layout_locations(version, base_path, dsid):
    """Return dataset-related path in a RIA store

    Parameters
    ----------
    version : int
      Layout version of the store.
    base_path : Path
      Base path of the store.
    dsid : str
      Dataset ID

    Returns
    -------
    Path, Path, Path
      The location of the bare dataset repository in the store,
      the directory with archive files for the dataset, and the
      annex object directory are return in that order.
    """
    if version == 1:
        dsgit_dir = base_path / dsid[:3] / dsid[3:]
        archive_dir = dsgit_dir / 'archives'
        dsobj_dir = dsgit_dir / 'annex' / 'objects'
        return dsgit_dir, archive_dir, dsobj_dir
    else:
        raise ValueError("Unknown layout version: {}. Supported: {}"
                         "".format(version, known_versions_dst))


def verify_ria_url(url, cfg):
    """Verify and decode ria url

    Expects a ria-URL pointing to a RIA store, applies rewrites and tries to
    decode potential host and base path for the store from it. Additionally
    raises if `url` is considered invalid.

    ria+ssh://somehost:/path/to/store
    ria+file:///path/to/store

    Parameters
    ----------
    url : str
      URL to verify an decode.
    cfg : dict-like
      Configuration settings for rewrite_url()

    Raises
    ------
    ValueError

    Returns
    -------
    tuple
      (host, base-path, rewritten url)
    """
    from datalad.config import rewrite_url
    from datalad.support.network import URL

    if not url:
        raise ValueError("Got no URL")

    url = rewrite_url(cfg, url)
    url_ri = URL(url)
    if not url_ri.scheme.startswith('ria+'):
        raise ValueError("Missing ria+ prefix in final URL: %s" % url)
    if url_ri.fragment:
        raise ValueError(
            "Unexpected fragment in RIA-store URL: %s" % url_ri.fragment)
    protocol = url_ri.scheme[4:]
    if protocol not in ['ssh', 'file', 'http', 'https']:
        raise ValueError("Unsupported protocol: %s. "
                         "Supported: ssh, file, http(s)" %
                         protocol)

    return url_ri.hostname if protocol != 'file' else None, \
        url_ri.path if url_ri.path else '/', \
        url


def _ensure_version(io, base_path, version):
    """Check a store or dataset version and make sure it is declared

    Parameters
    ----------
    io: SSHRemoteIO or LocalIO
    base_path: Path
      root path of a store or dataset
    version: str
      target layout version of the store (dataset tree)
    """
    version_file = base_path / 'ria-layout-version'
    if io.exists(version_file):
        existing_version = io.read_file(version_file).split('|')[0].strip()
        if existing_version != version.split('|')[0]:
            # We have an already existing location with a conflicting version on
            # record.
            # Note, that a config flag after pipe symbol is fine.
            raise ValueError("Conflicting version found at target: {}"
                             .format(existing_version))
        else:
            # already exists, recorded version fits - nothing to do
            return
    # Note, that the following does create the base-path dir as well, since
    # mkdir has parents=True:
    io.mkdir(base_path)
    io.write_file(version_file, version)


def create_store(io, base_path, version):
    """Helper to create a RIA store

    Note, that this is meant as an internal helper and part of intermediate
    RF'ing. Ultimately should lead to dedicated command or option for
    create-sibling-ria.

    Parameters
    ----------
    io: SSHRemoteIO or LocalIO
      Respective execution instance.
      Note: To be replaced by proper command abstraction
    base_path: Path
      root path of the store
    version: str
      layout version of the store (dataset tree)
    """

    # At store level the only version we know as of now is 1.
    if version not in known_versions_dst:
        raise UnknownLayoutVersion("RIA store layout version unknown: {}."
                                   "Supported versions: {}"
                                   .format(version, known_versions_dst))
    _ensure_version(io, base_path, version)
    error_logs = base_path / 'error_logs'
    io.mkdir(error_logs)


def create_ds_in_store(io, base_path, dsid, obj_version, store_version, alias=None):
    """Helper to create a dataset in a RIA store

    Note, that this is meant as an internal helper and part of intermediate
    RF'ing. Ultimately should lead to a version option for create-sibling-ria
    in conjunction with a store creation command/option.

    Parameters
    ----------
    io: SSHRemoteIO or LocalIO
      Respective execution instance.
      Note: To be replaced by proper command abstraction
    base_path: Path
      root path of the store
    dsid: str
      dataset id
    store_version: str
      layout version of the store (dataset tree)
    obj_version: str
      layout version of the dataset itself (object tree)
    alias: str, optional
      alias for the dataset in the store
    """

    # TODO: Note for RF'ing, that this is about setting up a valid target
    #       for the special remote not a replacement for create-sibling-ria.
    #       There's currently no git (bare) repo created.

    try:
        # TODO: This is currently store layout version!
        #       Too entangled by current get_layout_locations.
        dsgit_dir, archive_dir, dsobj_dir = \
            get_layout_locations(int(store_version), base_path, dsid)
    except ValueError as e:
        raise UnknownLayoutVersion(str(e))

    if obj_version not in known_versions_objt:
        raise UnknownLayoutVersion("Dataset layout version unknown: {}. "
                                   "Supported: {}"
                                   .format(obj_version, known_versions_objt))

    _ensure_version(io, dsgit_dir, obj_version)

    io.mkdir(archive_dir)
    io.mkdir(dsobj_dir)
    if alias:
        alias_dir = base_path / "alias"
        io.mkdir(alias_dir)
        try:
            # go for a relative path to keep the alias links valid
            # when moving a store
            io.symlink(
                Path('..') / dsgit_dir.relative_to(base_path),
                alias_dir / alias)
        except FileExistsError:
            lgr.warning("Alias %r already exists in the RIA store, not adding an "
                        "alias.", alias)

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