From ae4ad85e5f890798303f3d5a1c6c437173957a69 Mon Sep 17 00:00:00 2001 From: Chris Hunt Date: Tue, 29 Oct 2019 23:41:34 -0400 Subject: [PATCH 1/6] Make virtualenv/script fixture factories --- tests/conftest.py | 69 +++++++++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 7a54373b8ec..816a08877bd 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -272,16 +272,23 @@ def virtualenv_template(request, tmpdir_factory, pip_src, yield venv +@pytest.fixture(scope="session") +def virtualenv_factory(virtualenv_template): + def factory(tmpdir): + return VirtualEnvironment(tmpdir, virtualenv_template) + + return factory + + @pytest.fixture -def virtualenv(virtualenv_template, tmpdir, isolate): +def virtualenv(virtualenv_factory, tmpdir): """ Return a virtual environment which is unique to each test function invocation created inside of a sub directory of the test function's temporary directory. The returned object is a ``tests.lib.venv.VirtualEnvironment`` object. """ - venv_location = tmpdir.joinpath("workspace", "venv") - yield VirtualEnvironment(venv_location, virtualenv_template) + yield virtualenv_factory(tmpdir.joinpath("workspace", "venv")) @pytest.fixture @@ -289,35 +296,45 @@ def with_wheel(virtualenv, wheel_install): install_egg_link(virtualenv, 'wheel', wheel_install) +@pytest.fixture(scope="session") +def script_factory(virtualenv_factory, deprecated_python): + def factory(tmpdir, virtualenv=None): + if virtualenv is None: + virtualenv = virtualenv_factory(tmpdir.joinpath("venv")) + return PipTestEnvironment( + # The base location for our test environment + tmpdir, + + # Tell the Test Environment where our virtualenv is located + virtualenv=virtualenv, + + # Do not ignore hidden files, they need to be checked as well + ignore_hidden=False, + + # We are starting with an already empty directory + start_clear=False, + + # We want to ensure no temporary files are left behind, so the + # PipTestEnvironment needs to capture and assert against temp + capture_temp=True, + assert_no_temp=True, + + # Deprecated python versions produce an extra deprecation warning + pip_expect_warning=deprecated_python, + ) + + return factory + + @pytest.fixture -def script(tmpdir, virtualenv, deprecated_python): +def script(tmpdir, virtualenv, script_factory): """ Return a PipTestEnvironment which is unique to each test function and will execute all commands inside of the unique virtual environment for this test function. The returned object is a ``tests.lib.scripttest.PipTestEnvironment``. """ - return PipTestEnvironment( - # The base location for our test environment - tmpdir.joinpath("workspace"), - - # Tell the Test Environment where our virtualenv is located - virtualenv=virtualenv, - - # Do not ignore hidden files, they need to be checked as well - ignore_hidden=False, - - # We are starting with an already empty directory - start_clear=False, - - # We want to ensure no temporary files are left behind, so the - # PipTestEnvironment needs to capture and assert against temp - capture_temp=True, - assert_no_temp=True, - - # Deprecated python versions produce an extra deprecation warning - pip_expect_warning=deprecated_python, - ) + return script_factory(tmpdir.joinpath("workspace"), virtualenv) @pytest.fixture(scope="session") @@ -359,7 +376,7 @@ def in_memory_pip(): return InMemoryPip() -@pytest.fixture +@pytest.fixture(scope="session") def deprecated_python(): """Used to indicate whether pip deprecated this python version""" return sys.version_info[:2] in [(2, 7)] From cc73b2b933125952c7c9145a11a9c7bbad4d010e Mon Sep 17 00:00:00 2001 From: Chris Hunt Date: Wed, 30 Oct 2019 01:00:08 -0400 Subject: [PATCH 2/6] Use shared autocomplete_script for tests --- tests/functional/test_completion.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/tests/functional/test_completion.py b/tests/functional/test_completion.py index 9280b5d6a8a..2bb53ff2f0f 100644 --- a/tests/functional/test_completion.py +++ b/tests/functional/test_completion.py @@ -3,6 +3,8 @@ import pytest +from tests.lib.path import Path + COMPLETION_FOR_SUPPORTED_SHELLS_TESTS = ( ('bash', """\ _pip_completion() @@ -52,20 +54,28 @@ def test_completion_for_supported_shells(script, pip_src, common_wheels, assert completion in result.stdout, str(result.stdout) -def test_completion_for_unknown_shell(script): +@pytest.fixture(scope="session") +def autocomplete_script(tmpdir_factory, script_factory): + tmpdir = Path(str(tmpdir_factory.mktemp("autocomplete_script"))) + return script_factory(tmpdir.joinpath("workspace")) + + +def test_completion_for_unknown_shell(autocomplete_script): """ Test getting completion for an unknown shell """ error_msg = 'no such option: --myfooshell' - result = script.pip('completion', '--myfooshell', expect_error=True) + result = autocomplete_script.pip( + 'completion', '--myfooshell', expect_error=True + ) assert error_msg in result.stderr, 'tests for an unknown shell failed' -def test_completion_alone(script): +def test_completion_alone(autocomplete_script): """ Test getting completion for none shell, just pip completion """ - result = script.pip('completion', allow_stderr_error=True) + result = autocomplete_script.pip('completion', allow_stderr_error=True) assert 'ERROR: You must pass --bash or --fish or --zsh' in result.stderr, \ 'completion alone failed -- ' + result.stderr @@ -285,10 +295,12 @@ def test_completion_path_after_option(script, data): @pytest.mark.parametrize('flag', ['--bash', '--zsh', '--fish']) -def test_completion_uses_same_executable_name(script, flag, deprecated_python): +def test_completion_uses_same_executable_name( + autocomplete_script, flag, deprecated_python +): executable_name = 'pip{}'.format(sys.version_info[0]) # Deprecated python versions produce an extra deprecation warning - result = script.run( + result = autocomplete_script.run( executable_name, 'completion', flag, expect_stderr=deprecated_python, ) assert executable_name in result.stdout From ab6b17b2a6e7296cb4e822987b54868726aa97ac Mon Sep 17 00:00:00 2001 From: Chris Hunt Date: Wed, 30 Oct 2019 01:01:03 -0400 Subject: [PATCH 3/6] Refactor autocomplete test helper to use shared script --- tests/functional/test_completion.py | 91 ++++++++++++++--------------- 1 file changed, 45 insertions(+), 46 deletions(-) diff --git a/tests/functional/test_completion.py b/tests/functional/test_completion.py index 2bb53ff2f0f..a043bf56c60 100644 --- a/tests/functional/test_completion.py +++ b/tests/functional/test_completion.py @@ -60,6 +60,27 @@ def autocomplete_script(tmpdir_factory, script_factory): return script_factory(tmpdir.joinpath("workspace")) +@pytest.fixture +def autocomplete(autocomplete_script, monkeypatch): + monkeypatch.setattr(autocomplete_script, 'environ', os.environ.copy()) + autocomplete_script.environ['PIP_AUTO_COMPLETE'] = '1' + + def do_autocomplete(words, cword, cwd=None): + autocomplete_script.environ['COMP_WORDS'] = words + autocomplete_script.environ['COMP_CWORD'] = cword + result = autocomplete_script.run( + 'python', '-c', + 'from pip._internal.cli.autocompletion import autocomplete;' + 'autocomplete()', + expect_error=True, + cwd=cwd, + ) + + return result, autocomplete_script + + return do_autocomplete + + def test_completion_for_unknown_shell(autocomplete_script): """ Test getting completion for an unknown shell @@ -80,83 +101,64 @@ def test_completion_alone(autocomplete_script): 'completion alone failed -- ' + result.stderr -def setup_completion(script, words, cword, cwd=None): - script.environ = os.environ.copy() - script.environ['PIP_AUTO_COMPLETE'] = '1' - script.environ['COMP_WORDS'] = words - script.environ['COMP_CWORD'] = cword - - # expect_error is True because autocomplete exists with 1 status code - result = script.run( - 'python', '-c', - 'from pip._internal.cli.autocompletion import autocomplete;' - 'autocomplete()', - expect_error=True, - cwd=cwd, - ) - - return result, script - - -def test_completion_for_un_snippet(script): +def test_completion_for_un_snippet(autocomplete): """ Test getting completion for ``un`` should return uninstall """ - res, env = setup_completion(script, 'pip un', '1') + res, env = autocomplete('pip un', '1') assert res.stdout.strip().split() == ['uninstall'], res.stdout -def test_completion_for_default_parameters(script): +def test_completion_for_default_parameters(autocomplete): """ Test getting completion for ``--`` should contain --help """ - res, env = setup_completion(script, 'pip --', '1') + res, env = autocomplete('pip --', '1') assert '--help' in res.stdout,\ "autocomplete function could not complete ``--``" -def test_completion_option_for_command(script): +def test_completion_option_for_command(autocomplete): """ Test getting completion for ``--`` in command (e.g. ``pip search --``) """ - res, env = setup_completion(script, 'pip search --', '2') + res, env = autocomplete('pip search --', '2') assert '--help' in res.stdout,\ "autocomplete function could not complete ``--``" -def test_completion_short_option(script): +def test_completion_short_option(autocomplete): """ Test getting completion for short options after ``-`` (eg. pip -) """ - res, env = setup_completion(script, 'pip -', '1') + res, env = autocomplete('pip -', '1') assert '-h' in res.stdout.split(),\ "autocomplete function could not complete short options after ``-``" -def test_completion_short_option_for_command(script): +def test_completion_short_option_for_command(autocomplete): """ Test getting completion for short options after ``-`` in command (eg. pip search -) """ - res, env = setup_completion(script, 'pip search -', '2') + res, env = autocomplete('pip search -', '2') assert '-h' in res.stdout.split(),\ "autocomplete function could not complete short options after ``-``" -def test_completion_files_after_option(script, data): +def test_completion_files_after_option(autocomplete, data): """ Test getting completion for or after options in command (e.g. ``pip install -r``) """ - res, env = setup_completion( - script=script, + res, env = autocomplete( words=('pip install -r r'), cword='3', cwd=data.completion_paths, @@ -186,13 +188,12 @@ def test_completion_files_after_option(script, data): ) -def test_completion_not_files_after_option(script, data): +def test_completion_not_files_after_option(autocomplete, data): """ Test not getting completion files after options which not applicable (e.g. ``pip install``) """ - res, env = setup_completion( - script=script, + res, env = autocomplete( words=('pip install r'), cword='2', cwd=data.completion_paths, @@ -210,13 +211,14 @@ def test_completion_not_files_after_option(script, data): @pytest.mark.parametrize("cl_opts", ["-U", "--user", "-h"]) -def test_completion_not_files_after_nonexpecting_option(script, data, cl_opts): +def test_completion_not_files_after_nonexpecting_option( + autocomplete, data, cl_opts +): """ Test not getting completion files after options which not applicable (e.g. ``pip install``) """ - res, env = setup_completion( - script=script, + res, env = autocomplete( words=('pip install %s r' % cl_opts), cword='2', cwd=data.completion_paths, @@ -233,13 +235,12 @@ def test_completion_not_files_after_nonexpecting_option(script, data, cl_opts): ) -def test_completion_directories_after_option(script, data): +def test_completion_directories_after_option(autocomplete, data): """ Test getting completion after options in command (e.g. ``pip --cache-dir``) """ - res, env = setup_completion( - script=script, + res, env = autocomplete( words=('pip --cache-dir r'), cword='2', cwd=data.completion_paths, @@ -258,13 +259,12 @@ def test_completion_directories_after_option(script, data): ) -def test_completion_subdirectories_after_option(script, data): +def test_completion_subdirectories_after_option(autocomplete, data): """ Test getting completion after options in command given path of a directory """ - res, env = setup_completion( - script=script, + res, env = autocomplete( words=('pip --cache-dir ' + os.path.join('resources', '')), cword='2', cwd=data.completion_paths, @@ -276,13 +276,12 @@ def test_completion_subdirectories_after_option(script, data): ) -def test_completion_path_after_option(script, data): +def test_completion_path_after_option(autocomplete, data): """ Test getting completion after options in command given absolute path """ - res, env = setup_completion( - script=script, + res, env = autocomplete( words=('pip install -e ' + os.path.join(data.completion_paths, 'R')), cword='3', ) From 1f9028851d1ded51df1e56d838ea1c9a9fd274e1 Mon Sep 17 00:00:00 2001 From: Chris Hunt Date: Wed, 30 Oct 2019 01:02:12 -0400 Subject: [PATCH 4/6] Make shared script with launchers installed --- tests/functional/test_completion.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/tests/functional/test_completion.py b/tests/functional/test_completion.py index a043bf56c60..4eaf36b4c85 100644 --- a/tests/functional/test_completion.py +++ b/tests/functional/test_completion.py @@ -37,20 +37,31 @@ ) +@pytest.fixture(scope="session") +def script_with_launchers( + tmpdir_factory, script_factory, common_wheels, pip_src +): + tmpdir = Path(str(tmpdir_factory.mktemp("script_with_launchers"))) + script = script_factory(tmpdir.joinpath("workspace")) + # Re-install pip so we get the launchers. + script.pip_install_local('-f', common_wheels, pip_src) + return script + + @pytest.mark.parametrize( 'shell, completion', COMPLETION_FOR_SUPPORTED_SHELLS_TESTS, ids=[t[0] for t in COMPLETION_FOR_SUPPORTED_SHELLS_TESTS], ) -def test_completion_for_supported_shells(script, pip_src, common_wheels, - shell, completion): +def test_completion_for_supported_shells( + script_with_launchers, shell, completion +): """ Test getting completion for bash shell """ - # Re-install pip so we get the launchers. - script.pip_install_local('-f', common_wheels, pip_src) - - result = script.pip('completion', '--' + shell, use_module=False) + result = script_with_launchers.pip( + 'completion', '--' + shell, use_module=False + ) assert completion in result.stdout, str(result.stdout) From 6cd98526269dbababf81ff620bc1b09eb58203d7 Mon Sep 17 00:00:00 2001 From: Chris Hunt Date: Wed, 30 Oct 2019 01:38:13 -0400 Subject: [PATCH 5/6] Use shared script in pip list tests --- tests/conftest.py | 5 +++ tests/functional/test_list.py | 75 +++++++++++++++-------------------- 2 files changed, 36 insertions(+), 44 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 816a08877bd..baffc6bf181 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -343,6 +343,11 @@ def common_wheels(): return DATA_DIR.joinpath('common_wheels') +@pytest.fixture(scope="session") +def shared_data(tmpdir_factory): + return TestData.copy(Path(tmpdir_factory.mktemp("data"))) + + @pytest.fixture def data(tmpdir): return TestData.copy(tmpdir.joinpath("data")) diff --git a/tests/functional/test_list.py b/tests/functional/test_list.py index 764d326f7b8..4866b01e4b0 100644 --- a/tests/functional/test_list.py +++ b/tests/functional/test_list.py @@ -3,30 +3,35 @@ import pytest +from tests.lib.path import Path -def test_basic_list(script, data): - """ - Test default behavior of list command without format specifier. - """ +@pytest.fixture(scope="session") +def simple_script(tmpdir_factory, script_factory, shared_data): + tmpdir = Path(str(tmpdir_factory.mktemp("pip_test_package"))) + script = script_factory(tmpdir.joinpath("workspace")) script.pip( - 'install', '-f', data.find_links, '--no-index', 'simple==1.0', + 'install', '-f', shared_data.find_links, '--no-index', 'simple==1.0', 'simple2==3.0', ) - result = script.pip('list') + return script + + +def test_basic_list(simple_script): + """ + Test default behavior of list command without format specifier. + + """ + result = simple_script.pip('list') assert 'simple 1.0' in result.stdout, str(result) assert 'simple2 3.0' in result.stdout, str(result) -def test_verbose_flag(script, data): +def test_verbose_flag(simple_script): """ Test the list command with the '-v' option """ - script.pip( - 'install', '-f', data.find_links, '--no-index', 'simple==1.0', - 'simple2==3.0', - ) - result = script.pip('list', '-v', '--format=columns') + result = simple_script.pip('list', '-v', '--format=columns') assert 'Package' in result.stdout, str(result) assert 'Version' in result.stdout, str(result) assert 'Location' in result.stdout, str(result) @@ -35,15 +40,11 @@ def test_verbose_flag(script, data): assert 'simple2 3.0' in result.stdout, str(result) -def test_columns_flag(script, data): +def test_columns_flag(simple_script): """ Test the list command with the '--format=columns' option """ - script.pip( - 'install', '-f', data.find_links, '--no-index', 'simple==1.0', - 'simple2==3.0', - ) - result = script.pip('list', '--format=columns') + result = simple_script.pip('list', '--format=columns') assert 'Package' in result.stdout, str(result) assert 'Version' in result.stdout, str(result) assert 'simple (1.0)' not in result.stdout, str(result) @@ -51,22 +52,18 @@ def test_columns_flag(script, data): assert 'simple2 3.0' in result.stdout, str(result) -def test_format_priority(script, data): +def test_format_priority(simple_script): """ Test that latest format has priority over previous ones. """ - script.pip( - 'install', '-f', data.find_links, '--no-index', 'simple==1.0', - 'simple2==3.0', - ) - result = script.pip('list', '--format=columns', '--format=freeze', - expect_stderr=True) + result = simple_script.pip('list', '--format=columns', '--format=freeze', + expect_stderr=True) assert 'simple==1.0' in result.stdout, str(result) assert 'simple2==3.0' in result.stdout, str(result) assert 'simple 1.0' not in result.stdout, str(result) assert 'simple2 3.0' not in result.stdout, str(result) - result = script.pip('list', '--format=freeze', '--format=columns') + result = simple_script.pip('list', '--format=freeze', '--format=columns') assert 'Package' in result.stdout, str(result) assert 'Version' in result.stdout, str(result) assert 'simple==1.0' not in result.stdout, str(result) @@ -75,23 +72,21 @@ def test_format_priority(script, data): assert 'simple2 3.0' in result.stdout, str(result) -def test_local_flag(script, data): +def test_local_flag(simple_script): """ Test the behavior of --local flag in the list command """ - script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0') - result = script.pip('list', '--local', '--format=json') + result = simple_script.pip('list', '--local', '--format=json') assert {"name": "simple", "version": "1.0"} in json.loads(result.stdout) -def test_local_columns_flag(script, data): +def test_local_columns_flag(simple_script): """ Test the behavior of --local --format=columns flags in the list command """ - script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0') - result = script.pip('list', '--local', '--format=columns') + result = simple_script.pip('list', '--local', '--format=columns') assert 'Package' in result.stdout assert 'Version' in result.stdout assert 'simple (1.0)' not in result.stdout @@ -479,30 +474,22 @@ def test_not_required_flag(script, data): assert 'TopoRequires3 ' not in result.stdout -def test_list_freeze(script, data): +def test_list_freeze(simple_script): """ Test freeze formatting of list command """ - script.pip( - 'install', '-f', data.find_links, '--no-index', 'simple==1.0', - 'simple2==3.0', - ) - result = script.pip('list', '--format=freeze') + result = simple_script.pip('list', '--format=freeze') assert 'simple==1.0' in result.stdout, str(result) assert 'simple2==3.0' in result.stdout, str(result) -def test_list_json(script, data): +def test_list_json(simple_script): """ Test json formatting of list command """ - script.pip( - 'install', '-f', data.find_links, '--no-index', 'simple==1.0', - 'simple2==3.0', - ) - result = script.pip('list', '--format=json') + result = simple_script.pip('list', '--format=json') data = json.loads(result.stdout) assert {'name': 'simple', 'version': '1.0'} in data assert {'name': 'simple2', 'version': '3.0'} in data From 0c4625b2bac57815f652e28e93324ec95e087872 Mon Sep 17 00:00:00 2001 From: Chris Hunt Date: Wed, 30 Oct 2019 01:39:02 -0400 Subject: [PATCH 6/6] Use another shared script in pip list tests --- tests/conftest.py | 2 +- tests/functional/test_list.py | 61 ++++++++++++++++------------------- 2 files changed, 28 insertions(+), 35 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index baffc6bf181..6a05ca5ea00 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -345,7 +345,7 @@ def common_wheels(): @pytest.fixture(scope="session") def shared_data(tmpdir_factory): - return TestData.copy(Path(tmpdir_factory.mktemp("data"))) + return TestData.copy(Path(str(tmpdir_factory.mktemp("data")))) @pytest.fixture diff --git a/tests/functional/test_list.py b/tests/functional/test_list.py index 4866b01e4b0..a863c42c91a 100644 --- a/tests/functional/test_list.py +++ b/tests/functional/test_list.py @@ -242,50 +242,53 @@ def test_outdated_columns_flag(script, data): assert 'simple2' not in result.stdout, str(result) # 3.0 is latest +@pytest.fixture(scope="session") +def pip_test_package_script(tmpdir_factory, script_factory, shared_data): + tmpdir = Path(str(tmpdir_factory.mktemp("pip_test_package"))) + script = script_factory(tmpdir.joinpath("workspace")) + script.pip( + 'install', '-f', shared_data.find_links, '--no-index', 'simple==1.0' + ) + script.pip( + 'install', '-e', + 'git+https://github.com/pypa/pip-test-package.git#egg=pip-test-package' + ) + return script + + @pytest.mark.network -def test_editables_flag(script, data): +def test_editables_flag(pip_test_package_script): """ Test the behavior of --editables flag in the list command """ - script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0') - result = script.pip( - 'install', '-e', - 'git+https://github.com/pypa/pip-test-package.git#egg=pip-test-package' - ) - result = script.pip('list', '--editable', '--format=json') - result2 = script.pip('list', '--editable') + result = pip_test_package_script.pip('list', '--editable', '--format=json') + result2 = pip_test_package_script.pip('list', '--editable') assert {"name": "simple", "version": "1.0"} \ not in json.loads(result.stdout) assert os.path.join('src', 'pip-test-package') in result2.stdout @pytest.mark.network -def test_exclude_editable_flag(script, data): +def test_exclude_editable_flag(pip_test_package_script): """ Test the behavior of --editables flag in the list command """ - script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0') - result = script.pip( - 'install', '-e', - 'git+https://github.com/pypa/pip-test-package.git#egg=pip-test-package' + result = pip_test_package_script.pip( + 'list', '--exclude-editable', '--format=json' ) - result = script.pip('list', '--exclude-editable', '--format=json') assert {"name": "simple", "version": "1.0"} in json.loads(result.stdout) assert "pip-test-package" \ not in {p["name"] for p in json.loads(result.stdout)} @pytest.mark.network -def test_editables_columns_flag(script, data): +def test_editables_columns_flag(pip_test_package_script): """ Test the behavior of --editables flag in the list command """ - script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0') - result = script.pip( - 'install', '-e', - 'git+https://github.com/pypa/pip-test-package.git#egg=pip-test-package' + result = pip_test_package_script.pip( + 'list', '--editable', '--format=columns' ) - result = script.pip('list', '--editable', '--format=columns') assert 'Package' in result.stdout assert 'Version' in result.stdout assert 'Location' in result.stdout @@ -295,16 +298,11 @@ def test_editables_columns_flag(script, data): @pytest.mark.network -def test_uptodate_editables_flag(script, data): +def test_uptodate_editables_flag(pip_test_package_script, data): """ test the behavior of --editable --uptodate flag in the list command """ - script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0') - result = script.pip( - 'install', '-e', - 'git+https://github.com/pypa/pip-test-package.git#egg=pip-test-package' - ) - result = script.pip( + result = pip_test_package_script.pip( 'list', '-f', data.find_links, '--no-index', '--editable', '--uptodate', ) @@ -315,17 +313,12 @@ def test_uptodate_editables_flag(script, data): @pytest.mark.network -def test_uptodate_editables_columns_flag(script, data): +def test_uptodate_editables_columns_flag(pip_test_package_script, data): """ test the behavior of --editable --uptodate --format=columns flag in the list command """ - script.pip('install', '-f', data.find_links, '--no-index', 'simple==1.0') - result = script.pip( - 'install', '-e', - 'git+https://github.com/pypa/pip-test-package.git#egg=pip-test-package' - ) - result = script.pip( + result = pip_test_package_script.pip( 'list', '-f', data.find_links, '--no-index', '--editable', '--uptodate', '--format=columns', )