Skip to content

Commit c6933d5

Browse files
authored
Add a warning when run as root (e.g., sudo pip) (#9394)
1 parent 3ab760a commit c6933d5

File tree

5 files changed

+42
-4
lines changed

5 files changed

+42
-4
lines changed

news/6409.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add a warning, discouraging the usage of pip as root, outside a virtual environment.

src/pip/_internal/cli/base_command.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,8 @@ def _main(self, args):
149149
"The directory '%s' or its parent directory is not owned "
150150
"or is not writable by the current user. The cache "
151151
"has been disabled. Check the permissions and owner of "
152-
"that directory. If executing pip with sudo, you may want "
153-
"sudo's -H flag.",
152+
"that directory. If executing pip with sudo, you should "
153+
"use sudo's -H flag.",
154154
options.cache_dir,
155155
)
156156
options.cache_dir = None

src/pip/_internal/cli/req_command.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import logging
99
import os
10+
import sys
1011
from functools import partial
1112
from optparse import Values
1213
from typing import Any, List, Optional, Tuple
@@ -38,6 +39,7 @@
3839
TempDirectoryTypeRegistry,
3940
tempdir_kinds,
4041
)
42+
from pip._internal.utils.virtualenv import running_under_virtualenv
4143

4244
logger = logging.getLogger(__name__)
4345

@@ -152,6 +154,35 @@ def handle_pip_version_check(self, options):
152154
]
153155

154156

157+
def warn_if_run_as_root():
158+
# type: () -> None
159+
"""Output a warning for sudo users on Unix.
160+
161+
In a virtual environment, sudo pip still writes to virtualenv.
162+
On Windows, users may run pip as Administrator without issues.
163+
This warning only applies to Unix root users outside of virtualenv.
164+
"""
165+
if running_under_virtualenv():
166+
return
167+
if not hasattr(os, "getuid"):
168+
return
169+
# On Windows, there are no "system managed" Python packages. Installing as
170+
# Administrator via pip is the correct way of updating system environments.
171+
#
172+
# We choose sys.platform over utils.compat.WINDOWS here to enable Mypy platform
173+
# checks: https://mypy.readthedocs.io/en/stable/common_issues.html
174+
if sys.platform == "win32" or sys.platform == "cygwin":
175+
return
176+
if sys.platform == "darwin" or sys.platform == "linux":
177+
if os.getuid() != 0:
178+
return
179+
logger.warning(
180+
"Running pip as root will break packages and permissions. "
181+
"You should install packages reliably by using venv: "
182+
"https://pip.pypa.io/warnings/venv"
183+
)
184+
185+
155186
def with_cleanup(func):
156187
# type: (Any) -> Any
157188
"""Decorator for common logic related to managing temporary

src/pip/_internal/commands/install.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@
1212
from pip._internal.cache import WheelCache
1313
from pip._internal.cli import cmdoptions
1414
from pip._internal.cli.cmdoptions import make_target_python
15-
from pip._internal.cli.req_command import RequirementCommand, with_cleanup
15+
from pip._internal.cli.req_command import (
16+
RequirementCommand,
17+
warn_if_run_as_root,
18+
with_cleanup,
19+
)
1620
from pip._internal.cli.status_codes import ERROR, SUCCESS
1721
from pip._internal.exceptions import CommandError, InstallationError
1822
from pip._internal.locations import get_scheme
@@ -443,6 +447,7 @@ def run(self, options, args):
443447
options.target_dir, target_temp_dir, options.upgrade
444448
)
445449

450+
warn_if_run_as_root()
446451
return SUCCESS
447452

448453
def _handle_target_dir(self, target_dir, target_temp_dir, upgrade):

src/pip/_internal/commands/uninstall.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from pip._vendor.packaging.utils import canonicalize_name
55

66
from pip._internal.cli.base_command import Command
7-
from pip._internal.cli.req_command import SessionCommandMixin
7+
from pip._internal.cli.req_command import SessionCommandMixin, warn_if_run_as_root
88
from pip._internal.cli.status_codes import SUCCESS
99
from pip._internal.exceptions import InstallationError
1010
from pip._internal.req import parse_requirements
@@ -88,4 +88,5 @@ def run(self, options, args):
8888
if uninstall_pathset:
8989
uninstall_pathset.commit()
9090

91+
warn_if_run_as_root()
9192
return SUCCESS

0 commit comments

Comments
 (0)