mirror of
https://github.com/pypa/pip
synced 2023-12-13 21:30:23 +01:00
Merge pull request #9073 from xavfernandez/freeze_list_exclude
Add --exclude option to pip freeze and pip list commands
This commit is contained in:
commit
a0e34e9cf7
7 changed files with 68 additions and 2 deletions
1
news/4256.feature.rst
Normal file
1
news/4256.feature.rst
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Add ``--exclude`` option to ``pip freeze`` and ``pip list`` commands to explicitly exclude packages from the output.
|
2
news/4256.removal.rst
Normal file
2
news/4256.removal.rst
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
``pip freeze`` will stop filtering the ``pip``, ``setuptools``, ``distribute`` and ``wheel`` packages from ``pip freeze`` output in a future version.
|
||||||
|
To keep the previous behavior, users should use the new ``--exclude`` option.
|
|
@ -20,6 +20,8 @@ from functools import partial
|
||||||
from optparse import SUPPRESS_HELP, Option, OptionGroup
|
from optparse import SUPPRESS_HELP, Option, OptionGroup
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
|
|
||||||
|
from pip._vendor.packaging.utils import canonicalize_name
|
||||||
|
|
||||||
from pip._internal.cli.progress_bars import BAR_TYPES
|
from pip._internal.cli.progress_bars import BAR_TYPES
|
||||||
from pip._internal.exceptions import CommandError
|
from pip._internal.exceptions import CommandError
|
||||||
from pip._internal.locations import USER_CACHE_DIR, get_src_prefix
|
from pip._internal.locations import USER_CACHE_DIR, get_src_prefix
|
||||||
|
@ -133,9 +135,15 @@ def _path_option_check(option, opt, value):
|
||||||
return os.path.expanduser(value)
|
return os.path.expanduser(value)
|
||||||
|
|
||||||
|
|
||||||
|
def _package_name_option_check(option, opt, value):
|
||||||
|
# type: (Option, str, str) -> str
|
||||||
|
return canonicalize_name(value)
|
||||||
|
|
||||||
|
|
||||||
class PipOption(Option):
|
class PipOption(Option):
|
||||||
TYPES = Option.TYPES + ("path",)
|
TYPES = Option.TYPES + ("path", "package_name")
|
||||||
TYPE_CHECKER = Option.TYPE_CHECKER.copy()
|
TYPE_CHECKER = Option.TYPE_CHECKER.copy()
|
||||||
|
TYPE_CHECKER["package_name"] = _package_name_option_check
|
||||||
TYPE_CHECKER["path"] = _path_option_check
|
TYPE_CHECKER["path"] = _path_option_check
|
||||||
|
|
||||||
|
|
||||||
|
@ -866,6 +874,17 @@ def check_list_path_option(options):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
list_exclude = partial(
|
||||||
|
PipOption,
|
||||||
|
'--exclude',
|
||||||
|
dest='excludes',
|
||||||
|
action='append',
|
||||||
|
metavar='package',
|
||||||
|
type='package_name',
|
||||||
|
help="Exclude specified package from the output",
|
||||||
|
) # type: Callable[..., Option]
|
||||||
|
|
||||||
|
|
||||||
no_python_version_warning = partial(
|
no_python_version_warning = partial(
|
||||||
Option,
|
Option,
|
||||||
'--no-python-version-warning',
|
'--no-python-version-warning',
|
||||||
|
|
|
@ -74,6 +74,7 @@ class FreezeCommand(Command):
|
||||||
dest='exclude_editable',
|
dest='exclude_editable',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help='Exclude editable package from output.')
|
help='Exclude editable package from output.')
|
||||||
|
self.cmd_opts.add_option(cmdoptions.list_exclude())
|
||||||
|
|
||||||
self.parser.insert_option_group(0, self.cmd_opts)
|
self.parser.insert_option_group(0, self.cmd_opts)
|
||||||
|
|
||||||
|
@ -85,6 +86,9 @@ class FreezeCommand(Command):
|
||||||
if not options.freeze_all:
|
if not options.freeze_all:
|
||||||
skip.update(DEV_PKGS)
|
skip.update(DEV_PKGS)
|
||||||
|
|
||||||
|
if options.excludes:
|
||||||
|
skip.update(options.excludes)
|
||||||
|
|
||||||
cmdoptions.check_list_path_option(options)
|
cmdoptions.check_list_path_option(options)
|
||||||
|
|
||||||
if options.find_links:
|
if options.find_links:
|
||||||
|
|
|
@ -12,6 +12,7 @@ from pip._internal.exceptions import CommandError
|
||||||
from pip._internal.index.collector import LinkCollector
|
from pip._internal.index.collector import LinkCollector
|
||||||
from pip._internal.index.package_finder import PackageFinder
|
from pip._internal.index.package_finder import PackageFinder
|
||||||
from pip._internal.models.selection_prefs import SelectionPreferences
|
from pip._internal.models.selection_prefs import SelectionPreferences
|
||||||
|
from pip._internal.utils.compat import stdlib_pkgs
|
||||||
from pip._internal.utils.misc import (
|
from pip._internal.utils.misc import (
|
||||||
dist_is_editable,
|
dist_is_editable,
|
||||||
get_installed_distributions,
|
get_installed_distributions,
|
||||||
|
@ -114,6 +115,7 @@ class ListCommand(IndexGroupCommand):
|
||||||
help='Include editable package from output.',
|
help='Include editable package from output.',
|
||||||
default=True,
|
default=True,
|
||||||
)
|
)
|
||||||
|
self.cmd_opts.add_option(cmdoptions.list_exclude())
|
||||||
index_opts = cmdoptions.make_option_group(
|
index_opts = cmdoptions.make_option_group(
|
||||||
cmdoptions.index_group, self.parser
|
cmdoptions.index_group, self.parser
|
||||||
)
|
)
|
||||||
|
@ -147,12 +149,17 @@ class ListCommand(IndexGroupCommand):
|
||||||
|
|
||||||
cmdoptions.check_list_path_option(options)
|
cmdoptions.check_list_path_option(options)
|
||||||
|
|
||||||
|
skip = set(stdlib_pkgs)
|
||||||
|
if options.excludes:
|
||||||
|
skip.update(options.excludes)
|
||||||
|
|
||||||
packages = get_installed_distributions(
|
packages = get_installed_distributions(
|
||||||
local_only=options.local,
|
local_only=options.local,
|
||||||
user_only=options.user,
|
user_only=options.user,
|
||||||
editables_only=options.editable,
|
editables_only=options.editable,
|
||||||
include_editables=options.include_editable,
|
include_editables=options.include_editable,
|
||||||
paths=options.path,
|
paths=options.path,
|
||||||
|
skip=skip,
|
||||||
)
|
)
|
||||||
|
|
||||||
# get_not_required must be called firstly in order to find and
|
# get_not_required must be called firstly in order to find and
|
||||||
|
|
|
@ -16,6 +16,7 @@ from tests.lib import (
|
||||||
need_mercurial,
|
need_mercurial,
|
||||||
need_svn,
|
need_svn,
|
||||||
path_to_url,
|
path_to_url,
|
||||||
|
wheel,
|
||||||
)
|
)
|
||||||
|
|
||||||
distribute_re = re.compile('^distribute==[0-9.]+\n', re.MULTILINE)
|
distribute_re = re.compile('^distribute==[0-9.]+\n', re.MULTILINE)
|
||||||
|
@ -80,6 +81,25 @@ def test_freeze_with_pip(script):
|
||||||
assert 'pip==' in result.stdout
|
assert 'pip==' in result.stdout
|
||||||
|
|
||||||
|
|
||||||
|
def test_exclude_and_normalization(script, tmpdir):
|
||||||
|
req_path = wheel.make_wheel(
|
||||||
|
name="Normalizable_Name", version="1.0").save_to_dir(tmpdir)
|
||||||
|
script.pip("install", "--no-index", req_path)
|
||||||
|
result = script.pip("freeze")
|
||||||
|
assert "Normalizable-Name" in result.stdout
|
||||||
|
result = script.pip("freeze", "--exclude", "normalizablE-namE")
|
||||||
|
assert "Normalizable-Name" not in result.stdout
|
||||||
|
|
||||||
|
|
||||||
|
def test_freeze_multiple_exclude_with_all(script, with_wheel):
|
||||||
|
result = script.pip('freeze', '--all')
|
||||||
|
assert 'pip==' in result.stdout
|
||||||
|
assert 'wheel==' in result.stdout
|
||||||
|
result = script.pip('freeze', '--all', '--exclude', 'pip', '--exclude', 'wheel')
|
||||||
|
assert 'pip==' not in result.stdout
|
||||||
|
assert 'wheel==' not in result.stdout
|
||||||
|
|
||||||
|
|
||||||
def test_freeze_with_invalid_names(script):
|
def test_freeze_with_invalid_names(script):
|
||||||
"""
|
"""
|
||||||
Test that invalid names produce warnings and are passed over gracefully.
|
Test that invalid names produce warnings and are passed over gracefully.
|
||||||
|
|
|
@ -3,7 +3,7 @@ import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from tests.lib import create_test_package_with_setup
|
from tests.lib import create_test_package_with_setup, wheel
|
||||||
from tests.lib.path import Path
|
from tests.lib.path import Path
|
||||||
|
|
||||||
|
|
||||||
|
@ -94,6 +94,19 @@ def test_local_columns_flag(simple_script):
|
||||||
assert 'simple 1.0' in result.stdout, str(result)
|
assert 'simple 1.0' in result.stdout, str(result)
|
||||||
|
|
||||||
|
|
||||||
|
def test_multiple_exclude_and_normalization(script, tmpdir):
|
||||||
|
req_path = wheel.make_wheel(
|
||||||
|
name="Normalizable_Name", version="1.0").save_to_dir(tmpdir)
|
||||||
|
script.pip("install", "--no-index", req_path)
|
||||||
|
result = script.pip("list")
|
||||||
|
print(result.stdout)
|
||||||
|
assert "Normalizable-Name" in result.stdout
|
||||||
|
assert "pip" in result.stdout
|
||||||
|
result = script.pip("list", "--exclude", "normalizablE-namE", "--exclude", "pIp")
|
||||||
|
assert "Normalizable-Name" not in result.stdout
|
||||||
|
assert "pip" not in result.stdout
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.network
|
@pytest.mark.network
|
||||||
@pytest.mark.incompatible_with_test_venv
|
@pytest.mark.incompatible_with_test_venv
|
||||||
def test_user_flag(script, data):
|
def test_user_flag(script, data):
|
||||||
|
|
Loading…
Reference in a new issue