Skip to content

bpo-42998: add 'user' parameter to pathlib.Path.home() #25271

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
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
12 changes: 9 additions & 3 deletions Doc/library/pathlib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -702,19 +702,25 @@ call fails (for example because the path doesn't exist).
PosixPath('/home/antoine/pathlib')


.. classmethod:: Path.home()
.. classmethod:: Path.home(user=None)

Return a new path object representing the user's home directory (as
Return a new path object representing a user's home directory (as
returned by :func:`os.path.expanduser` with ``~`` construct). If the home
directory can't be resolved, :exc:`RuntimeError` is raised.
directory can't be resolved, :exc:`RuntimeError` is raised. If no user name
is given, the current user's home directory is used.

::

>>> Path.home()
PosixPath('/home/antoine')
>>> Path.home('barney')
PosixPath('/home/barney')

.. versionadded:: 3.5

.. versionchanged:: 3.10
The *user* parameter was added.


.. method:: Path.stat(*, follow_symlinks=True)

Expand Down
18 changes: 12 additions & 6 deletions Lib/pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -1055,14 +1055,22 @@ def cwd(cls):
"""Return a new path pointing to the current working directory
(as returned by os.getcwd()).
"""
return cls(cls()._accessor.getcwd())
return cls(cls._accessor.getcwd())

@classmethod
def home(cls):
def home(cls, user=None):
"""Return a new path pointing to the user's home directory (as
returned by os.path.expanduser('~')).
"""
return cls("~").expanduser()
tilde = "~"
if user is None:
user = ""
elif isinstance(user, bytes):
tilde = b"~"
homedir = cls._accessor.expanduser(tilde + user)
if homedir[:1] == tilde:
raise RuntimeError("Could not determine home directory.")
return cls(homedir)

def samefile(self, other_path):
"""Return whether other_path is the same or not as this file
Expand Down Expand Up @@ -1488,9 +1496,7 @@ def expanduser(self):
"""
if (not (self._drv or self._root) and
self._parts and self._parts[0][:1] == '~'):
homedir = self._accessor.expanduser(self._parts[0])
if homedir[:1] == "~":
raise RuntimeError("Could not determine home directory.")
homedir = self.home(self.parts[0][1:])
return self._from_parts([homedir] + self._parts[1:])

return self
Expand Down
73 changes: 73 additions & 0 deletions Lib/test/test_pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -2559,6 +2559,42 @@ def test_expanduser(self):
self.assertEqual(p6.expanduser(), p6)
self.assertRaises(RuntimeError, p7.expanduser)

@unittest.skipUnless(hasattr(pwd, 'getpwall'),
'pwd module does not expose getpwall()')
@unittest.skipIf(sys.platform == "vxworks",
"no home directory on VxWorks")
def test_home(self):
P = self.cls
import_helper.import_module('pwd')
import pwd
pwdent = pwd.getpwuid(os.getuid())
username = pwdent.pw_name
userhome = pwdent.pw_dir.rstrip('/') or '/'
# Find arbitrary different user (if exists).
for pwdent in pwd.getpwall():
othername = pwdent.pw_name
otherhome = pwdent.pw_dir.rstrip('/')
if othername != username and otherhome:
break
else:
othername = username
otherhome = userhome

with os_helper.EnvironmentVarGuard() as env:
env.pop('HOME', None)

self.assertEqual(P.home(), P(userhome))
self.assertEqual(P.home(username), P(userhome))
self.assertEqual(P.home(othername), P(otherhome))
self.assertRaises(RuntimeError, P.home, 'missinguser')

env['HOME'] = '/tmp'

self.assertEqual(P.home(), P('/tmp'))
self.assertEqual(P.home(username), P(userhome))
self.assertEqual(P.home(othername), P(otherhome))
self.assertRaises(RuntimeError, P.home, 'missinguser')

@unittest.skipIf(sys.platform != "darwin",
"Bad file descriptor in /dev/fd affects only macOS")
def test_handling_bad_descriptor(self):
Expand Down Expand Up @@ -2654,6 +2690,43 @@ def check():
env['HOME'] = 'C:\\Users\\eve'
check()

def test_home(self):
P = self.cls
with os_helper.EnvironmentVarGuard() as env:
env.pop('HOME', None)
env.pop('USERPROFILE', None)
env.pop('HOMEPATH', None)
env.pop('HOMEDRIVE', None)
env['USERNAME'] = 'alice'

self.assertRaises(RuntimeError, P.home)
self.assertRaises(RuntimeError, P.home, 'alice')
self.assertRaises(RuntimeError, P.home, 'bob')

def check():
env.pop('USERNAME', None)
self.assertEqual(P.home(), P('C:/Users/alice'))
self.assertRaises(RuntimeError, P.home, 'alice')
env['USERNAME'] = 'alice'
self.assertEqual(P.home('alice'), P('C:/Users/alice'))
self.assertEqual(P.home('bob'), P('C:/Users/bob'))

env['HOMEPATH'] = 'C:\\Users\\alice'
check()

env['HOMEDRIVE'] = 'C:\\'
env['HOMEPATH'] = 'Users\\alice'
check()

env.pop('HOMEDRIVE', None)
env.pop('HOMEPATH', None)
env['USERPROFILE'] = 'C:\\Users\\alice'
check()

# bpo-38883: ignore `HOME` when set on windows
env['HOME'] = 'C:\\Users\\eve'
check()


class CompatiblePathTest(unittest.TestCase):
"""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add *user* parameter to `pathlib.Path.home()`