Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/4692.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This change adds a warning for situations where pip is run with sudo.
1 change: 1 addition & 0 deletions news/4727.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This change adds a warning for situations where pip is run with sudo.
1 change: 1 addition & 0 deletions news/7269.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This change fixes pip freeze by suppressing warnings for invalid packages.
1 change: 1 addition & 0 deletions news/7307.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This change prevents the unintended creation of temporary package directories.
1 change: 1 addition & 0 deletions news/7450.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This change prevents the unintended creation of temporary package directories.
1 change: 1 addition & 0 deletions news/7763.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This change prevents the unintended creation of temporary package directories.
1 change: 1 addition & 0 deletions news/7940.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This change adds a warning for situations where pip is run with sudo.
1 change: 1 addition & 0 deletions news/9235.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This change fixes pip freeze by suppressing warnings for invalid packages.
7 changes: 6 additions & 1 deletion src/pip/_internal/commands/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -721,11 +721,16 @@ def create_os_error_message(error, show_traceback, using_user_site):
if error.errno == errno.EACCES:
user_option_part = "Consider using the `--user` option"
permissions_part = "Check the permissions"
chown_part = ("Oftentimes packages are unnecessarily installed with "
"`sudo pip`, which sets the folder owner to root. "
"In this case, consider changing the owner to $USER: "
"`$ sudo chown -R $USER [package folder]`")

if not using_user_site:
parts.extend([
user_option_part, " or ",
permissions_part.lower(),
permissions_part.lower(), ".\n",
chown_part
])
else:
parts.append(permissions_part)
Expand Down
15 changes: 14 additions & 1 deletion src/pip/_internal/req/req_uninstall.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,8 +398,21 @@ def remove(self, auto_confirm=False, verbose=False):
for_rename = compress_for_rename(self.paths)

for path in sorted(compact(for_rename)):
moved.stash(path)
logger.debug('Removing file or directory %s', path)
try:
moved.stash(path)
except PermissionError as ex:
raise UninstallationError(
"{!r}\nFailed to uninstall {!r} because "
"this user lacks permissions to the package "
"folder. Consider using the `--user` option or "
"check the permissions.\nOftentimes packages "
"are unnecessarily installed with `sudo pip`, "
"which sets the folder owner to root. In this "
"case, consider changing the owner to $USER: "
"`$ sudo chown -R $USER [package folder]`"
.format(str(ex), dist_name_version)
)

for pth in self.pth.values():
pth.remove()
Expand Down
6 changes: 6 additions & 0 deletions src/pip/_internal/utils/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,11 @@ def renames(old, new):
"""Like os.renames(), but handles renaming across devices."""
# Implementation borrowed from os.renames().
head, tail = os.path.split(new)
if not os.access(old, os.W_OK):
raise PermissionError(
'Rename aborted. User does not have write access to ' + old
)

if head and tail and not os.path.exists(head):
os.makedirs(head)

Expand Down Expand Up @@ -458,6 +463,7 @@ def user_test(d):
return [d for d in working_set
if local_test(d) and
d.key not in skip and
'-' not in d.key[0] and
editable_test(d) and
editables_only_test(d) and
user_test(d)
Expand Down
8 changes: 7 additions & 1 deletion tests/functional/test_freeze.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,12 @@ def fake_install(pkgname, dest):

valid_pkgnames = ('middle-dash', 'middle_underscore', 'middle.dot')
invalid_pkgnames = (
'-leadingdash', '_leadingunderscore', '.leadingdot',
'trailingdash-', 'trailingunderscore_', 'trailingdot.'
)
ignored_pkgnames = (
'-leadingdash', '_leadingunderscore', '.leadingdot'
)

for pkgname in valid_pkgnames + invalid_pkgnames:
fake_install(pkgname, script.site_packages_path)
result = script.pip('freeze', expect_stderr=True)
Expand All @@ -142,6 +145,9 @@ def fake_install(pkgname, dest):
'distribution {}...'.format(dist_repr)
)
_check_output(result.stderr, expected)
for pkgname in ignored_pkgnames:
assert(pkgname not in result.stdout)
assert(pkgname not in result.stderr)

# Also check that the parse error details occur at least once.
# We only need to find one occurrence to know that exception details
Expand Down
20 changes: 14 additions & 6 deletions tests/unit/test_command_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,13 @@ def test_rejection_for_location_requirement_options():
# show_traceback = True, using_user_site = False
(OSError("Illegal byte sequence"), True, False, 'Could not'
' install packages due to an OSError.\n'),
(OSError(errno.EACCES, "No file permission"), True, False, 'Could'
' not install packages due to an OSError.\nConsider using the'
' `--user` option or check the permissions.\n'),
(OSError(errno.EACCES, "No file permission"), True, False,
'Could not install packages due to an OSError.'
'\nConsider using the `--user` option or check the permissions.'
'\nOftentimes packages are unnecessarily installed with `sudo pip`, '
'which sets the folder owner to root. In this '
'case, consider changing the owner to $USER: '
'`$ sudo chown -R $USER [package folder]`.\n'),
# show_traceback = False, using_user_site = True
(OSError("Illegal byte sequence"), False, True, 'Could not'
' install packages due to an OSError: Illegal byte'
Expand All @@ -104,9 +108,13 @@ def test_rejection_for_location_requirement_options():
' install packages due to an OSError: Illegal byte sequence'
'\n'),
(OSError(errno.EACCES, "No file permission"), False, False,
'Could not install packages due to an OSError: [Errno 13] No'
' file permission\nConsider using the `--user` option or check the'
' permissions.\n'),
'Could not install packages due to an OSError: '
'[Errno 13] No file permission\nConsider using the `--user` '
'option or check the permissions.'
'\nOftentimes packages are unnecessarily installed with `sudo pip`, '
'which sets the folder owner to root. In this '
'case, consider changing the owner to $USER: '
'`$ sudo chown -R $USER [package folder]`.\n')
])
def test_create_os_error_message(
error, show_traceback, using_user_site, expected
Expand Down