Skip to content

Commit fe28652

Browse files
committed
Merge pull request #3136 from rouge8/fix-py26-home-slash
Fix user directory expansion when HOME=/
2 parents f8377a4 + 73a439e commit fe28652

File tree

10 files changed

+69
-15
lines changed

10 files changed

+69
-15
lines changed

CHANGES.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
* When installing, if building a wheel fails, clear up the build directory
3030
before falling back to a source install. :issue:`3047`.
3131

32+
* Fix user directory expansion when ``HOME=/``. Workaround for Python bug
33+
http://bugs.python.org/issue14768, reported in :issue:`2996`.
34+
3235

3336
**7.1.2 (2015-08-22)**
3437

pip/compat/__init__.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,18 @@ def get_path_uid(path):
101101
return file_uid
102102

103103

104+
def expanduser(path):
105+
"""
106+
Expand ~ and ~user constructions.
107+
108+
Includes a workaround for http://bugs.python.org/issue14768
109+
"""
110+
expanded = os.path.expanduser(path)
111+
if path.startswith('~/') and expanded.startswith('//'):
112+
expanded = expanded[1:]
113+
return expanded
114+
115+
104116
# packages in the stdlib that may have installation metadata, but should not be
105117
# considered 'installed'. this theoretically could be determined based on
106118
# dist.location (py27:`sysconfig.get_paths()['stdlib']`,

pip/locations.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from distutils import sysconfig
1010
from distutils.command.install import install, SCHEME_KEYS # noqa
1111

12-
from pip.compat import WINDOWS
12+
from pip.compat import WINDOWS, expanduser
1313
from pip.utils import appdirs
1414

1515

@@ -114,7 +114,7 @@ def virtualenv_no_global():
114114

115115
site_packages = sysconfig.get_python_lib()
116116
user_site = site.USER_SITE
117-
user_dir = os.path.expanduser('~')
117+
user_dir = expanduser('~')
118118
if WINDOWS:
119119
bin_py = os.path.join(sys.prefix, 'Scripts')
120120
bin_user = os.path.join(user_site, 'Scripts')

pip/req/req_set.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from pip._vendor import pkg_resources
1010
from pip._vendor import requests
1111

12+
from pip.compat import expanduser
1213
from pip.download import (url_to_path, unpack_url)
1314
from pip.exceptions import (InstallationError, BestVersionAlreadyInstalled,
1415
DistributionNotFound, PreviousBuildDirError)
@@ -290,7 +291,7 @@ def has_requirements(self):
290291
@property
291292
def is_download(self):
292293
if self.download_dir:
293-
self.download_dir = os.path.expanduser(self.download_dir)
294+
self.download_dir = expanduser(self.download_dir)
294295
if os.path.exists(self.download_dir):
295296
return True
296297
else:

pip/utils/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import zipfile
1616

1717
from pip.exceptions import InstallationError
18-
from pip.compat import console_to_str, stdlib_pkgs
18+
from pip.compat import console_to_str, expanduser, stdlib_pkgs
1919
from pip.locations import (
2020
site_packages, user_site, running_under_virtualenv, virtualenv_no_global,
2121
write_delete_marker_file,
@@ -251,7 +251,7 @@ def normalize_path(path, resolve_symlinks=True):
251251
Convert a path to its canonical, case-normalized, absolute version.
252252
253253
"""
254-
path = os.path.expanduser(path)
254+
path = expanduser(path)
255255
if resolve_symlinks:
256256
path = os.path.realpath(path)
257257
else:

pip/utils/appdirs.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import os
88
import sys
99

10-
from pip.compat import WINDOWS
10+
from pip.compat import WINDOWS, expanduser
1111

1212

1313
def user_cache_dir(appname):
@@ -39,13 +39,13 @@ def user_cache_dir(appname):
3939
path = os.path.join(path, appname, "Cache")
4040
elif sys.platform == "darwin":
4141
# Get the base path
42-
path = os.path.expanduser("~/Library/Caches")
42+
path = expanduser("~/Library/Caches")
4343

4444
# Add our app name to it
4545
path = os.path.join(path, appname)
4646
else:
4747
# Get the base path
48-
path = os.getenv("XDG_CACHE_HOME", os.path.expanduser("~/.cache"))
48+
path = os.getenv("XDG_CACHE_HOME", expanduser("~/.cache"))
4949

5050
# Add our app name to it
5151
path = os.path.join(path, appname)
@@ -85,12 +85,12 @@ def user_data_dir(appname, roaming=False):
8585
path = os.path.join(os.path.normpath(_get_win_folder(const)), appname)
8686
elif sys.platform == "darwin":
8787
path = os.path.join(
88-
os.path.expanduser('~/Library/Application Support/'),
88+
expanduser('~/Library/Application Support/'),
8989
appname,
9090
)
9191
else:
9292
path = os.path.join(
93-
os.getenv('XDG_DATA_HOME', os.path.expanduser("~/.local/share")),
93+
os.getenv('XDG_DATA_HOME', expanduser("~/.local/share")),
9494
appname,
9595
)
9696

@@ -122,7 +122,7 @@ def user_config_dir(appname, roaming=True):
122122
elif sys.platform == "darwin":
123123
path = user_data_dir(appname)
124124
else:
125-
path = os.getenv('XDG_CONFIG_HOME', os.path.expanduser("~/.config"))
125+
path = os.getenv('XDG_CONFIG_HOME', expanduser("~/.config"))
126126
path = os.path.join(path, appname)
127127

128128
return path
@@ -156,7 +156,7 @@ def site_config_dirs(appname):
156156
xdg_config_dirs = os.getenv('XDG_CONFIG_DIRS', '/etc/xdg')
157157
if xdg_config_dirs:
158158
pathlist = [
159-
os.sep.join([os.path.expanduser(x), appname])
159+
os.sep.join([expanduser(x), appname])
160160
for x in xdg_config_dirs.split(os.pathsep)
161161
]
162162
else:

pip/wheel.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from pip._vendor.six import StringIO
2525

2626
import pip
27+
from pip.compat import expanduser
2728
from pip.download import path_to_url, unpack_url
2829
from pip.exceptions import (
2930
InstallationError, InvalidWheelFilename, UnsupportedWheel)
@@ -55,7 +56,7 @@ def __init__(self, cache_dir, format_control):
5556
:param format_control: A pip.index.FormatControl object to limit
5657
binaries being read from the cache.
5758
"""
58-
self._cache_dir = os.path.expanduser(cache_dir) if cache_dir else None
59+
self._cache_dir = expanduser(cache_dir) if cache_dir else None
5960
self._format_control = format_control
6061

6162
def cached_wheel(self, link, package_name):

tests/unit/test_appdirs.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ def test_user_cache_dir_linux_override(self, monkeypatch):
4444

4545
assert appdirs.user_cache_dir("pip") == "/home/test/.other-cache/pip"
4646

47+
def test_user_cache_dir_linux_home_slash(self, monkeypatch):
48+
# Verify that we are not affected by http://bugs.python.org/issue14768
49+
monkeypatch.delenv("XDG_CACHE_HOME")
50+
monkeypatch.setenv("HOME", "/")
51+
monkeypatch.setattr(sys, "platform", "linux2")
52+
53+
assert appdirs.user_cache_dir("pip") == "/.cache/pip"
54+
4755

4856
class TestSiteConfigDirs:
4957

@@ -154,6 +162,14 @@ def test_user_data_dir_linux_override(self, monkeypatch):
154162

155163
assert appdirs.user_data_dir("pip") == "/home/test/.other-share/pip"
156164

165+
def test_user_data_dir_linux_home_slash(self, monkeypatch):
166+
# Verify that we are not affected by http://bugs.python.org/issue14768
167+
monkeypatch.delenv("XDG_DATA_HOME")
168+
monkeypatch.setenv("HOME", "/")
169+
monkeypatch.setattr(sys, "platform", "linux2")
170+
171+
assert appdirs.user_data_dir("pip") == "/.local/share/pip"
172+
157173

158174
class TestUserConfigDir:
159175

@@ -213,3 +229,11 @@ def test_user_config_dir_linux_override(self, monkeypatch):
213229
monkeypatch.setattr(sys, "platform", "linux2")
214230

215231
assert appdirs.user_config_dir("pip") == "/home/test/.other-config/pip"
232+
233+
def test_user_config_dir_linux_home_slash(self, monkeypatch):
234+
# Verify that we are not affected by http://bugs.python.org/issue14768
235+
monkeypatch.delenv("XDG_CONFIG_HOME")
236+
monkeypatch.setenv("HOME", "/")
237+
monkeypatch.setattr(sys, "platform", "linux2")
238+
239+
assert appdirs.user_config_dir("pip") == "/.config/pip"

tests/unit/test_compat.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import os
2-
from pip.compat import get_path_uid, native_str
2+
from pip.compat import expanduser, get_path_uid, native_str
33
import pytest
44

55

@@ -42,3 +42,15 @@ def test_to_native_str_type():
4242
some_unicode = b"test\xE9 et approuv\xE9".decode('iso-8859-15')
4343
assert isinstance(native_str(some_bytes, True), str)
4444
assert isinstance(native_str(some_unicode, True), str)
45+
46+
47+
@pytest.mark.parametrize("home,path,expanded", [
48+
("/Users/test", "~", "/Users/test"),
49+
("/Users/test", "~/.cache", "/Users/test/.cache"),
50+
# Verify that we are not affected by http://bugs.python.org/issue14768
51+
("/", "~", "/"),
52+
("/", "~/.cache", "/.cache"),
53+
])
54+
def test_expanduser(home, path, expanded, monkeypatch):
55+
monkeypatch.setenv("HOME", home)
56+
assert expanduser(path) == expanded

tests/unit/test_wheel.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from pip._vendor import pkg_resources
88
from pip import pep425tags, wheel
9+
from pip.compat import expanduser
910
from pip.exceptions import InvalidWheelFilename, UnsupportedWheel
1011
from pip.utils import unpack_file
1112

@@ -417,7 +418,7 @@ class TestWheelCache:
417418

418419
def test_expands_path(self):
419420
wc = wheel.WheelCache("~/.foo/", None)
420-
assert wc._cache_dir == os.path.expanduser("~/.foo/")
421+
assert wc._cache_dir == expanduser("~/.foo/")
421422

422423
def test_falsey_path_none(self):
423424
wc = wheel.WheelCache(False, None)

0 commit comments

Comments
 (0)