Skip to content

Commit

Permalink
feat(pip/_internal): Skip current directory when performing freeze
Browse files Browse the repository at this point in the history
This skips any *.egg_info present in the current directory when
performing pip freeze.

This fixes pypa#2926
  • Loading branch information
gutsytechster committed Mar 1, 2020
1 parent bf0da0f commit 1c7ada5
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 25 deletions.
1 change: 1 addition & 0 deletions news/2926.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Skip local directory by default for 'pip freeze' or when invoked as 'python -m pip freeze'
15 changes: 13 additions & 2 deletions src/pip/_internal/utils/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
import io
import logging
import os
import posixpath
import shutil
import stat
import sys
from collections import deque

import posixpath
from pip._vendor import pkg_resources
# NOTE: retrying is not annotated in typeshed as on 2017-07-17, which is
# why we ignore the type on this import.
Expand Down Expand Up @@ -397,6 +397,14 @@ def dist_is_editable(dist):
return False


def dist_in_curr_dir(dist):
'''
Return True if given Distribution is installed in the current directory.
'''
norm_path = normalize_path(dist_location(dist))
return norm_path.startswith(normalize_path(os.getcwd()))


def get_installed_distributions(
local_only=True, # type: bool
skip=stdlib_pkgs, # type: Container[str]
Expand Down Expand Up @@ -456,12 +464,15 @@ def editables_only_test(d):
def user_test(d):
return True

curr_dir_test = dist_in_curr_dir

return [d for d in working_set
if local_test(d) and
d.key not in skip and
editable_test(d) and
editables_only_test(d) and
user_test(d)
user_test(d) and
not curr_dir_test(d)
]


Expand Down
48 changes: 31 additions & 17 deletions tests/functional/test_freeze.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,34 +79,36 @@ def test_freeze_with_pip(script):
assert 'pip==' in result.stdout


def _fake_install(pkgname, dest):
egg_info_path = os.path.join(
dest, '{}-1.0-py{}.{}.egg-info'.format(
pkgname.replace('-', '_'),
sys.version_info[0],
sys.version_info[1]
)
)
with open(egg_info_path, 'w') as egg_info_file:
egg_info_file.write(textwrap.dedent("""\
Metadata-Version: 1.0
Name: {}
Version: 1.0
""".format(pkgname)
))
return egg_info_path


def test_freeze_with_invalid_names(script):
"""
Test that invalid names produce warnings and are passed over gracefully.
"""

def fake_install(pkgname, dest):
egg_info_path = os.path.join(
dest, '{}-1.0-py{}.{}.egg-info'.format(
pkgname.replace('-', '_'),
sys.version_info[0],
sys.version_info[1]
)
)
with open(egg_info_path, 'w') as egg_info_file:
egg_info_file.write(textwrap.dedent("""\
Metadata-Version: 1.0
Name: {}
Version: 1.0
""".format(pkgname)
))

valid_pkgnames = ('middle-dash', 'middle_underscore', 'middle.dot')
invalid_pkgnames = (
'-leadingdash', '_leadingunderscore', '.leadingdot',
'trailingdash-', 'trailingunderscore_', 'trailingdot.'
)
for pkgname in valid_pkgnames + invalid_pkgnames:
fake_install(pkgname, script.site_packages_path)
_fake_install(pkgname, script.site_packages_path)
result = script.pip('freeze', expect_stderr=True)
for pkgname in valid_pkgnames:
_check_output(
Expand All @@ -129,6 +131,18 @@ def fake_install(pkgname, dest):
_check_output(result.stderr, expected)


def test_freeze_skip_curr_dir(script):
"""
Test that an .egginfo is skipped when present in current directory
"""

curr_dir = os.getcwd()
egg_info_path = _fake_install("foo-package", curr_dir)
result = script.pip("freeze")
os.remove(egg_info_path)
assert "foo-package==" not in result.stdout


@pytest.mark.git
def test_freeze_editable_not_vcs(script, tmpdir):
"""
Expand Down
29 changes: 23 additions & 6 deletions tests/unit/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ def test_noegglink_in_sitepkgs_venv_global(self):
assert egg_link_path(self.mock_dist) is None


@patch('pip._internal.utils.misc.dist_in_curr_dir')
@patch('pip._internal.utils.misc.dist_in_usersite')
@patch('pip._internal.utils.misc.dist_is_local')
@patch('pip._internal.utils.misc.dist_is_editable')
Expand All @@ -199,6 +200,7 @@ class Tests_get_installed_distributions:
Mock(test_name="editable"),
Mock(test_name="normal"),
Mock(test_name="user"),
Mock(test_name="curr_dir")
]

workingset_stdlib = [
Expand All @@ -221,45 +223,56 @@ def dist_is_local(self, dist):
def dist_in_usersite(self, dist):
return dist.test_name == "user"

def dist_in_curr_dir(self, dist):
return dist.test_name == "curr_dir"

@patch('pip._vendor.pkg_resources.working_set', workingset)
def test_editables_only(self, mock_dist_is_editable,
mock_dist_is_local,
mock_dist_in_usersite):
mock_dist_in_usersite,
mock_dist_in_curr_dir):
mock_dist_is_editable.side_effect = self.dist_is_editable
mock_dist_is_local.side_effect = self.dist_is_local
mock_dist_in_usersite.side_effect = self.dist_in_usersite
mock_dist_in_curr_dir.side_effect = self.dist_in_curr_dir
dists = get_installed_distributions(editables_only=True)
assert len(dists) == 1, dists
assert dists[0].test_name == "editable"

@patch('pip._vendor.pkg_resources.working_set', workingset)
def test_exclude_editables(self, mock_dist_is_editable,
mock_dist_is_local,
mock_dist_in_usersite):
mock_dist_in_usersite,
mock_dist_in_curr_dir):
mock_dist_is_editable.side_effect = self.dist_is_editable
mock_dist_is_local.side_effect = self.dist_is_local
mock_dist_in_usersite.side_effect = self.dist_in_usersite
mock_dist_in_curr_dir.side_effect = self.dist_in_curr_dir
dists = get_installed_distributions(include_editables=False)
assert len(dists) == 1
assert dists[0].test_name == "normal"

@patch('pip._vendor.pkg_resources.working_set', workingset)
def test_include_globals(self, mock_dist_is_editable,
mock_dist_is_local,
mock_dist_in_usersite):
mock_dist_in_usersite,
mock_dist_in_curr_dir):
mock_dist_is_editable.side_effect = self.dist_is_editable
mock_dist_is_local.side_effect = self.dist_is_local
mock_dist_in_usersite.side_effect = self.dist_in_usersite
mock_dist_in_curr_dir.side_effect = self.dist_in_curr_dir
dists = get_installed_distributions(local_only=False)
assert len(dists) == 4

@patch('pip._vendor.pkg_resources.working_set', workingset)
def test_user_only(self, mock_dist_is_editable,
mock_dist_is_local,
mock_dist_in_usersite):
mock_dist_in_usersite,
mock_dist_in_curr_dir):
mock_dist_is_editable.side_effect = self.dist_is_editable
mock_dist_is_local.side_effect = self.dist_is_local
mock_dist_in_usersite.side_effect = self.dist_in_usersite
mock_dist_in_curr_dir.side_effect = self.dist_in_curr_dir
dists = get_installed_distributions(local_only=False,
user_only=True)
assert len(dists) == 1
Expand All @@ -268,20 +281,24 @@ def test_user_only(self, mock_dist_is_editable,
@patch('pip._vendor.pkg_resources.working_set', workingset_stdlib)
def test_gte_py27_excludes(self, mock_dist_is_editable,
mock_dist_is_local,
mock_dist_in_usersite):
mock_dist_in_usersite,
mock_dist_in_curr_dir):
mock_dist_is_editable.side_effect = self.dist_is_editable
mock_dist_is_local.side_effect = self.dist_is_local
mock_dist_in_usersite.side_effect = self.dist_in_usersite
mock_dist_in_curr_dir.side_effect = self.dist_in_curr_dir
dists = get_installed_distributions()
assert len(dists) == 0

@patch('pip._vendor.pkg_resources.working_set', workingset_freeze)
def test_freeze_excludes(self, mock_dist_is_editable,
mock_dist_is_local,
mock_dist_in_usersite):
mock_dist_in_usersite,
mock_dist_in_curr_dir):
mock_dist_is_editable.side_effect = self.dist_is_editable
mock_dist_is_local.side_effect = self.dist_is_local
mock_dist_in_usersite.side_effect = self.dist_in_usersite
mock_dist_in_curr_dir.side_effect = self.dist_in_curr_dir
dists = get_installed_distributions(
skip=('setuptools', 'pip', 'distribute'))
assert len(dists) == 0
Expand Down

0 comments on commit 1c7ada5

Please sign in to comment.