Skip to content

appdirs: return paths relative to effective user's homedir #5360

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
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
48 changes: 48 additions & 0 deletions news/5359.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
When executing a pip install from within a Python process in which the euid has
been changed, the appdirs helpers which use `expanduser()` return paths
relative to the home directory of the actual user, rather than the effective
user. This causes the install to fail when the effective user cannot write to
the cachedir:

.. code-block:: python

>>> import subprocess
>>> import os
>>> os.geteuid()
0
>>> os.seteuid(1000)
>>> os.geteuid()
1000
>>> p = subprocess.Popen(['/home/erik/virtualenv/pipdev/bin/pip', 'install', 'pudb'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
>>> stdout, stderr = p.communicate()
>>> p.returncode
1
>>> stderr
"Could not install packages due to an EnvironmentError: [Errno 13] Permission denied: '/root/.cache/pip/wheels/ec/bb/79/09cd8a9982b637b251208ac3400f9702c72e63accc8e4b69e3'\nConsider using the `--user` option or check the permissions.\n\n"
>>>

With this bugfix, the installation proceeds as expected, and the sources/wheels
are downloaded to the effective user's cachedir:

.. code-block:: python

>>> import subprocess
>>> import os
>>> os.geteuid()
0
>>> os.seteuid(1000)
>>> os.geteuid()
1000
>>> p = subprocess.Popen(['/home/erik/virtualenv/pipdev/bin/pip', 'install', 'pudb'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
>>> stdout, stderr = p.communicate()
>>> p.returncode
0
>>> print(stdout)
Collecting pudb
Collecting urwid>=1.1.1 (from pudb)
Collecting pygments>=1.0 (from pudb)
Using cached https://files.pythonhosted.org/packages/02/ee/b6e02dc6529e82b75bb06823ff7d005b141037cb1416b10c6f00fc419dca/Pygments-2.2.0-py2.py3-none-any.whl
Installing collected packages: urwid, pygments, pudb
Successfully installed pudb-2017.1.4 pygments-2.2.0 urwid-2.0.1

>>>
73 changes: 48 additions & 25 deletions src/pip/_internal/utils/appdirs.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@

from pip._internal.compat import WINDOWS, expanduser

try:
import pwd
except ImportError:
pwd = None


def _effective_user():
try:
return pwd.getpwuid(os.geteuid()).pw_name
except Exception:
return ''


def user_cache_dir(appname):
r"""
Expand Down Expand Up @@ -44,15 +56,17 @@ def user_cache_dir(appname):

# Add our app name and Cache directory to it
path = os.path.join(path, appname, "Cache")
elif sys.platform == "darwin":
# Get the base path
path = expanduser("~/Library/Caches")

# Add our app name to it
path = os.path.join(path, appname)
else:
# Get the base path
path = os.getenv("XDG_CACHE_HOME", expanduser("~/.cache"))
user = _effective_user()
if sys.platform == "darwin":
# Get the base path
path = expanduser("~{0}/Library/Caches".format(user))
else:
# Get the base path
path = os.getenv(
"XDG_CACHE_HOME",
expanduser("~{0}/.cache".format(user))
)

# Add our app name to it
path = os.path.join(path, appname)
Expand Down Expand Up @@ -91,23 +105,28 @@ def user_data_dir(appname, roaming=False):
if WINDOWS:
const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA"
path = os.path.join(os.path.normpath(_get_win_folder(const)), appname)
elif sys.platform == "darwin":
path = os.path.join(
expanduser('~/Library/Application Support/'),
appname,
) if os.path.isdir(os.path.join(
expanduser('~/Library/Application Support/'),
appname,
)
) else os.path.join(
expanduser('~/.config/'),
appname,
)
else:
path = os.path.join(
os.getenv('XDG_DATA_HOME', expanduser("~/.local/share")),
appname,
)
user = _effective_user()
if sys.platform == "darwin":
path = os.path.join(
expanduser('~{0}/Library/Application Support/'.format(user)),
appname,
) if os.path.isdir(os.path.join(
expanduser('~{0}/Library/Application Support/'.format(user)),
appname,
)
) else os.path.join(
expanduser('~{0}/.config/'.format(user)),
appname,
)
else:
path = os.path.join(
os.getenv(
'XDG_DATA_HOME',
expanduser("~{0}/.local/share".format(user))
),
appname,
)

return path

Expand Down Expand Up @@ -137,7 +156,11 @@ def user_config_dir(appname, roaming=True):
elif sys.platform == "darwin":
path = user_data_dir(appname)
else:
path = os.getenv('XDG_CONFIG_HOME', expanduser("~/.config"))
user = _effective_user()
path = os.getenv(
'XDG_CONFIG_HOME',
expanduser("~{0}/.config".format(user))
)
path = os.path.join(path, appname)

return path
Expand Down