Skip to content
Merged
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
9 changes: 8 additions & 1 deletion Lib/posixpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,14 +259,21 @@ def expanduser(path):
if i == 1:
if 'HOME' not in os.environ:
import pwd
userhome = pwd.getpwuid(os.getuid()).pw_dir
try:
userhome = pwd.getpwuid(os.getuid()).pw_dir
except KeyError:
# bpo-10496: if the current user identifier doesn't exist in the
# password database, return the path unchanged
return path
else:
userhome = os.environ['HOME']
else:
import pwd
try:
pwent = pwd.getpwnam(path[1:i])
except KeyError:
# bpo-10496: if the user name from the path doesn't exist in the
# password database, return the path unchanged
return path
userhome = pwent.pw_dir
userhome = userhome.rstrip('/')
Expand Down
68 changes: 45 additions & 23 deletions Lib/test/test_posixpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,34 +262,56 @@ def fake_lstat(path):

def test_expanduser(self):
self.assertEqual(posixpath.expanduser("foo"), "foo")
with test_support.EnvironmentVarGuard() as env:

def test_expanduser_home_envvar(self):
with support.EnvironmentVarGuard() as env:
env['HOME'] = '/home/victor'
self.assertEqual(posixpath.expanduser("~"), "/home/victor")

# expanduser() strips trailing slash
env['HOME'] = '/home/victor/'
self.assertEqual(posixpath.expanduser("~"), "/home/victor")

for home in '/', '', '//', '///':
env['HOME'] = home
self.assertEqual(posixpath.expanduser("~"), "/")
self.assertEqual(posixpath.expanduser("~/"), "/")
self.assertEqual(posixpath.expanduser("~/foo"), "/foo")
try:
import pwd
except ImportError:
pass
else:
self.assertIsInstance(posixpath.expanduser("~/"), basestring)
# if home directory == root directory, this test makes no sense
if posixpath.expanduser("~") != '/':
self.assertEqual(
posixpath.expanduser("~") + "/",
posixpath.expanduser("~/")
)
self.assertIsInstance(posixpath.expanduser("~root/"), basestring)
self.assertIsInstance(posixpath.expanduser("~foo/"), basestring)

with test_support.EnvironmentVarGuard() as env:
# expanduser should fall back to using the password database
del env['HOME']
home = pwd.getpwuid(os.getuid()).pw_dir
# $HOME can end with a trailing /, so strip it (see #17809)
home = home.rstrip("/") or '/'
self.assertEqual(posixpath.expanduser("~"), home)

def test_expanduser_pwd(self):
pwd = support.import_module('pwd')

self.assertIsInstance(posixpath.expanduser("~/"), str)

# if home directory == root directory, this test makes no sense
if posixpath.expanduser("~") != '/':
self.assertEqual(
posixpath.expanduser("~") + "/",
posixpath.expanduser("~/")
)
self.assertIsInstance(posixpath.expanduser("~root/"), str)
self.assertIsInstance(posixpath.expanduser("~foo/"), str)

with support.EnvironmentVarGuard() as env:
# expanduser should fall back to using the password database
del env['HOME']

home = pwd.getpwuid(os.getuid()).pw_dir
# $HOME can end with a trailing /, so strip it (see #17809)
home = home.rstrip("/") or '/'
self.assertEqual(posixpath.expanduser("~"), home)

# bpo-10496: If the HOME environment variable is not set and the
# user (current identifier or name in the path) doesn't exist in
# the password database (pwd.getuid() or pwd.getpwnam() fail),
# expanduser() must return the path unchanged.
def raise_keyerror(*args):
raise KeyError

with support.swap_attr(pwd, 'getpwuid', raise_keyerror), \
support.swap_attr(pwd, 'getpwnam', raise_keyerror):
for path in ('~', '~/.local', '~vstinner/'):
self.assertEqual(posixpath.expanduser(path), path)

def test_normpath(self):
self.assertEqual(posixpath.normpath(""), ".")
Expand Down
44 changes: 44 additions & 0 deletions Lib/test/test_site.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import unittest
from test.test_support import run_unittest, TESTFN, EnvironmentVarGuard
from test.test_support import captured_output
from test import support
import __builtin__
import errno
import os
Expand Down Expand Up @@ -241,6 +242,7 @@ def test_getusersitepackages(self):
# the call sets USER_BASE *and* USER_SITE
self.assertEqual(site.USER_SITE, user_site)
self.assertTrue(user_site.startswith(site.USER_BASE), user_site)
self.assertEqual(site.USER_BASE, site.getuserbase())

def test_getsitepackages(self):
site.PREFIXES = ['xoxo']
Expand All @@ -265,6 +267,48 @@ def test_getsitepackages(self):
wanted = os.path.join('xoxo', 'lib', 'site-packages')
self.assertEqual(dirs[1], wanted)

def test_no_home_directory(self):
# bpo-10496: getuserbase() and getusersitepackages() must not fail if
# the current user has no home directory (if expanduser() returns the
# path unchanged).
site.USER_SITE = None
site.USER_BASE = None
sysconfig._CONFIG_VARS = None

with EnvironmentVarGuard() as environ, \
support.swap_attr(os.path, 'expanduser', lambda path: path):

del environ['PYTHONUSERBASE']
del environ['APPDATA']

user_base = site.getuserbase()
self.assertTrue(user_base.startswith('~' + os.sep),
user_base)

user_site = site.getusersitepackages()
self.assertTrue(user_site.startswith(user_base), user_site)

def fake_isdir(path):
fake_isdir.arg = path
return False
fake_isdir.arg = None

def must_not_be_called(*args):
raise AssertionError

with support.swap_attr(os.path, 'isdir', fake_isdir), \
support.swap_attr(site, 'addsitedir', must_not_be_called), \
support.swap_attr(site, 'ENABLE_USER_SITE', True):

# addusersitepackages() must not add user_site to sys.path
# if it is not an existing directory
known_paths = set()
site.addusersitepackages(known_paths)

self.assertEqual(fake_isdir.arg, user_site)
self.assertFalse(known_paths)


class PthFile(object):
"""Helper class for handling testing of .pth files"""

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
:func:`posixpath.expanduser` now returns the input *path* unchanged if the
``HOME`` environment variable is not set and the current user has no home
directory (if the current user identifier doesn't exist in the password
database). This change fix the :mod:`site` module if the current user doesn't
exist in the password database (if the user has no home directory).