diff -r 4a48450f2505 Doc/library/pathlib.rst --- a/Doc/library/pathlib.rst Sat Jun 14 20:41:22 2014 -0700 +++ b/Doc/library/pathlib.rst Tue Jun 17 07:46:51 2014 +0300 @@ -936,3 +936,15 @@ Remove this file or symbolic link. If the path points to a directory, use :func:`Path.rmdir` instead. + +.. method:: Path.expanduser() + + Return a new :class:`Path` with expanded ``~`` and ``~user`` constructs, + as returned by :meth:`os.path.expanduser`. + + >>> p = PosixPath('~films') + >>> p.expanduser() + PosixPath('/home/ericidle/films') + + .. versionadded:: 3.5 + diff -r 4a48450f2505 Lib/pathlib.py --- a/Lib/pathlib.py Sat Jun 14 20:41:22 2014 -0700 +++ b/Lib/pathlib.py Tue Jun 17 07:46:51 2014 +0300 @@ -972,6 +972,12 @@ other_st = os.stat(other_path) return os.path.samestat(st, other_st) + def expanduser(self): + """ Return a new path with expanded ~ and ~user constructs + (as returned by os.path.expanduser) + """ + return self.__class__(os.path.expanduser(str(self))) + def iterdir(self): """Iterate over the files in this directory. Does not yield any result for the special paths '.' and '..'. diff -r 4a48450f2505 Lib/test/test_pathlib.py --- a/Lib/test/test_pathlib.py Sat Jun 14 20:41:22 2014 -0700 +++ b/Lib/test/test_pathlib.py Tue Jun 17 07:46:51 2014 +0300 @@ -1276,6 +1276,11 @@ p = self.cls('') self.assertEqual(p.stat(), os.stat('.')) + def test_expanduser(self): + p = self.cls('~') + self.assertEqual(os.path.expanduser(str(p)), + str(p.expanduser())) + def test_exists(self): P = self.cls p = P(BASE) @@ -1870,6 +1875,22 @@ self.assertEqual(given, expect) self.assertEqual(set(p.rglob("FILEd*")), set()) + def test_expanduser_posix(self): + support.import_module("pwd") + import pwd + with support.EnvironmentVarGuard() as env: + env['HOME'] = '/' + p1 = self.cls('~') + p2 = self.cls('~/foo') + self.assertEqual(str(p1.expanduser()), "/") + self.assertEqual(str(p2.expanduser()), "/foo") + # 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) + self.assertEqual(str(self.cls("~").expanduser()), + home.rstrip("/")) + @only_nt class WindowsPathTest(_BasePathTest, unittest.TestCase): @@ -1885,6 +1906,32 @@ p = P(BASE, "dirC") self.assertEqual(set(p.rglob("FILEd")), { P(BASE, "dirC/dirD/fileD") }) + def test_expanduser_nt(self): + with support.EnvironmentVarGuard() as env: + env.pop('HOME', None) + env.pop('USERPROFILE', None) + env.pop('HOMEPATH', None) + + # test that the path returns unchanged + p1 = self.cls('~') + self.assertEqual(str(p1.expanduser()), '~') + + # test the first lookup key in the env vars + env['HOME'] = 'C:\\test' + p2 = self.cls('~') + self.assertEqual(str(p2.expanduser()), 'C:\\test') + + # test that HOMEPATH is available instead + env.pop('HOME', None) + env['HOMEPATH'] = 'C:\\idle' + p3 = self.cls('~') + self.assertEqual(str(p3.expanduser()), 'C:\\idle') + + env['HOMEDRIVE'] = 'C:\\' + env['HOMEPATH'] = 'eggs' + p4 = self.cls('~') + self.assertEqual(str(p4.expanduser()), 'C:\\eggs') + if __name__ == "__main__": unittest.main()