Issue #19776: Add a expanduser() method on Path objects.
Patch by Serhiy.
This commit is contained in:
parent
864d57c244
commit
8477ed6048
|
@ -670,6 +670,18 @@ call fails (for example because the path doesn't exist):
|
|||
symlink *points to* an existing file or directory.
|
||||
|
||||
|
||||
.. method:: Path.expanduser()
|
||||
|
||||
Return a new path with expanded ``~`` and ``~user`` constructs,
|
||||
as returned by :meth:`os.path.expanduser`::
|
||||
|
||||
>>> p = PosixPath('~/films/Monty Python')
|
||||
>>> p.expanduser()
|
||||
PosixPath('/home/eric/films/Monty Python')
|
||||
|
||||
.. versionadded:: 3.5
|
||||
|
||||
|
||||
.. method:: Path.glob(pattern)
|
||||
|
||||
Glob the given *pattern* in the directory represented by this path,
|
||||
|
@ -1003,7 +1015,4 @@ call fails (for example because the path doesn't exist):
|
|||
>>> p.read_text()
|
||||
'Text file contents'
|
||||
|
||||
An existing file of the same name is overwritten. The optional parameters
|
||||
have the same meaning as in :func:`open`.
|
||||
|
||||
.. versionadded:: 3.5
|
||||
|
|
|
@ -221,6 +221,36 @@ class _WindowsFlavour(_Flavour):
|
|||
# It's a path on a network drive => 'file://host/share/a/b'
|
||||
return 'file:' + urlquote_from_bytes(path.as_posix().encode('utf-8'))
|
||||
|
||||
def gethomedir(self, username):
|
||||
if 'HOME' in os.environ:
|
||||
userhome = os.environ['HOME']
|
||||
elif 'USERPROFILE' in os.environ:
|
||||
userhome = os.environ['USERPROFILE']
|
||||
elif 'HOMEPATH' in os.environ:
|
||||
try:
|
||||
drv = os.environ['HOMEDRIVE']
|
||||
except KeyError:
|
||||
drv = ''
|
||||
userhome = drv + os.environ['HOMEPATH']
|
||||
else:
|
||||
raise RuntimeError("Can't determine home directory")
|
||||
|
||||
if username:
|
||||
# Try to guess user home directory. By default all users
|
||||
# directories are located in the same place and are named by
|
||||
# corresponding usernames. If current user home directory points
|
||||
# to nonstandard place, this guess is likely wrong.
|
||||
if os.environ['USERNAME'] != username:
|
||||
drv, root, parts = self.parse_parts((userhome,))
|
||||
if parts[-1] != os.environ['USERNAME']:
|
||||
raise RuntimeError("Can't determine home directory "
|
||||
"for %r" % username)
|
||||
parts[-1] = username
|
||||
if drv or root:
|
||||
userhome = drv + root + self.join(parts[1:])
|
||||
else:
|
||||
userhome = self.join(parts)
|
||||
return userhome
|
||||
|
||||
class _PosixFlavour(_Flavour):
|
||||
sep = '/'
|
||||
|
@ -304,6 +334,21 @@ class _PosixFlavour(_Flavour):
|
|||
bpath = bytes(path)
|
||||
return 'file://' + urlquote_from_bytes(bpath)
|
||||
|
||||
def gethomedir(self, username):
|
||||
if not username:
|
||||
try:
|
||||
return os.environ['HOME']
|
||||
except KeyError:
|
||||
import pwd
|
||||
return pwd.getpwuid(os.getuid()).pw_dir
|
||||
else:
|
||||
import pwd
|
||||
try:
|
||||
return pwd.getpwnam(username).pw_dir
|
||||
except KeyError:
|
||||
raise RuntimeError("Can't determine home directory "
|
||||
"for %r" % username)
|
||||
|
||||
|
||||
_windows_flavour = _WindowsFlavour()
|
||||
_posix_flavour = _PosixFlavour()
|
||||
|
@ -1333,6 +1378,17 @@ class Path(PurePath):
|
|||
# (see https://bitbucket.org/pitrou/pathlib/issue/12/)
|
||||
return False
|
||||
|
||||
def expanduser(self):
|
||||
""" Return a new path with expanded ~ and ~user constructs
|
||||
(as returned by os.path.expanduser)
|
||||
"""
|
||||
if (not (self._drv or self._root) and
|
||||
self._parts and self._parts[0][:1] == '~'):
|
||||
homedir = self._flavour.gethomedir(self._parts[0][1:])
|
||||
return self._from_parts([homedir] + self._parts[1:])
|
||||
|
||||
return self
|
||||
|
||||
|
||||
class PosixPath(Path, PurePosixPath):
|
||||
__slots__ = ()
|
||||
|
|
|
@ -1286,6 +1286,19 @@ class _BasePathTest(object):
|
|||
p = self.cls('')
|
||||
self.assertEqual(p.stat(), os.stat('.'))
|
||||
|
||||
def test_expanduser_common(self):
|
||||
P = self.cls
|
||||
p = P('~')
|
||||
self.assertEqual(p.expanduser(), P(os.path.expanduser('~')))
|
||||
p = P('foo')
|
||||
self.assertEqual(p.expanduser(), p)
|
||||
p = P('/~')
|
||||
self.assertEqual(p.expanduser(), p)
|
||||
p = P('../~')
|
||||
self.assertEqual(p.expanduser(), p)
|
||||
p = P(P('').absolute().anchor) / '~'
|
||||
self.assertEqual(p.expanduser(), p)
|
||||
|
||||
def test_exists(self):
|
||||
P = self.cls
|
||||
p = P(BASE)
|
||||
|
@ -1959,6 +1972,48 @@ class PosixPathTest(_BasePathTest, unittest.TestCase):
|
|||
self.assertEqual(given, expect)
|
||||
self.assertEqual(set(p.rglob("FILEd*")), set())
|
||||
|
||||
def test_expanduser(self):
|
||||
P = self.cls
|
||||
support.import_module('pwd')
|
||||
import pwd
|
||||
pwdent = pwd.getpwuid(os.getuid())
|
||||
username = pwdent.pw_name
|
||||
userhome = pwdent.pw_dir.rstrip('/')
|
||||
# find arbitrary different user (if exists)
|
||||
for pwdent in pwd.getpwall():
|
||||
othername = pwdent.pw_name
|
||||
otherhome = pwdent.pw_dir.rstrip('/')
|
||||
if othername != username:
|
||||
break
|
||||
|
||||
p1 = P('~/Documents')
|
||||
p2 = P('~' + username + '/Documents')
|
||||
p3 = P('~' + othername + '/Documents')
|
||||
p4 = P('../~' + username + '/Documents')
|
||||
p5 = P('/~' + username + '/Documents')
|
||||
p6 = P('')
|
||||
p7 = P('~fakeuser/Documents')
|
||||
|
||||
with support.EnvironmentVarGuard() as env:
|
||||
env.pop('HOME', None)
|
||||
|
||||
self.assertEqual(p1.expanduser(), P(userhome) / 'Documents')
|
||||
self.assertEqual(p2.expanduser(), P(userhome) / 'Documents')
|
||||
self.assertEqual(p3.expanduser(), P(otherhome) / 'Documents')
|
||||
self.assertEqual(p4.expanduser(), p4)
|
||||
self.assertEqual(p5.expanduser(), p5)
|
||||
self.assertEqual(p6.expanduser(), p6)
|
||||
self.assertRaises(RuntimeError, p7.expanduser)
|
||||
|
||||
env['HOME'] = '/tmp'
|
||||
self.assertEqual(p1.expanduser(), P('/tmp/Documents'))
|
||||
self.assertEqual(p2.expanduser(), P(userhome) / 'Documents')
|
||||
self.assertEqual(p3.expanduser(), P(otherhome) / 'Documents')
|
||||
self.assertEqual(p4.expanduser(), p4)
|
||||
self.assertEqual(p5.expanduser(), p5)
|
||||
self.assertEqual(p6.expanduser(), p6)
|
||||
self.assertRaises(RuntimeError, p7.expanduser)
|
||||
|
||||
|
||||
@only_nt
|
||||
class WindowsPathTest(_BasePathTest, unittest.TestCase):
|
||||
|
@ -1974,6 +2029,61 @@ class WindowsPathTest(_BasePathTest, unittest.TestCase):
|
|||
p = P(BASE, "dirC")
|
||||
self.assertEqual(set(p.rglob("FILEd")), { P(BASE, "dirC/dirD/fileD") })
|
||||
|
||||
def test_expanduser(self):
|
||||
P = self.cls
|
||||
with support.EnvironmentVarGuard() as env:
|
||||
env.pop('HOME', None)
|
||||
env.pop('USERPROFILE', None)
|
||||
env.pop('HOMEPATH', None)
|
||||
env.pop('HOMEDRIVE', None)
|
||||
env['USERNAME'] = 'alice'
|
||||
|
||||
# test that the path returns unchanged
|
||||
p1 = P('~/My Documents')
|
||||
p2 = P('~alice/My Documents')
|
||||
p3 = P('~bob/My Documents')
|
||||
p4 = P('/~/My Documents')
|
||||
p5 = P('d:~/My Documents')
|
||||
p6 = P('')
|
||||
self.assertRaises(RuntimeError, p1.expanduser)
|
||||
self.assertRaises(RuntimeError, p2.expanduser)
|
||||
self.assertRaises(RuntimeError, p3.expanduser)
|
||||
self.assertEqual(p4.expanduser(), p4)
|
||||
self.assertEqual(p5.expanduser(), p5)
|
||||
self.assertEqual(p6.expanduser(), p6)
|
||||
|
||||
def check():
|
||||
env.pop('USERNAME', None)
|
||||
self.assertEqual(p1.expanduser(),
|
||||
P('C:/Users/alice/My Documents'))
|
||||
self.assertRaises(KeyError, p2.expanduser)
|
||||
env['USERNAME'] = 'alice'
|
||||
self.assertEqual(p2.expanduser(),
|
||||
P('C:/Users/alice/My Documents'))
|
||||
self.assertEqual(p3.expanduser(),
|
||||
P('C:/Users/bob/My Documents'))
|
||||
self.assertEqual(p4.expanduser(), p4)
|
||||
self.assertEqual(p5.expanduser(), p5)
|
||||
self.assertEqual(p6.expanduser(), p6)
|
||||
|
||||
# test the first lookup key in the env vars
|
||||
env['HOME'] = 'C:\\Users\\alice'
|
||||
check()
|
||||
|
||||
# test that HOMEPATH is available instead
|
||||
env.pop('HOME', None)
|
||||
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()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
Loading…
Reference in New Issue