• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import inspect
2import os
3import posixpath
4import sys
5import unittest
6from posixpath import realpath, abspath, dirname, basename
7from test import test_genericpath
8from test.support import get_attribute, import_helper
9from test.support import cpython_only, os_helper
10from test.support.os_helper import FakePath
11from unittest import mock
12
13try:
14    import posix
15except ImportError:
16    posix = None
17
18
19# An absolute path to a temporary filename for testing. We can't rely on TESTFN
20# being an absolute path, so we need this.
21
22ABSTFN = abspath(os_helper.TESTFN)
23
24def skip_if_ABSTFN_contains_backslash(test):
25    """
26    On Windows, posixpath.abspath still returns paths with backslashes
27    instead of posix forward slashes. If this is the case, several tests
28    fail, so skip them.
29    """
30    found_backslash = '\\' in ABSTFN
31    msg = "ABSTFN is not a posix path - tests fail"
32    return [test, unittest.skip(msg)(test)][found_backslash]
33
34def safe_rmdir(dirname):
35    try:
36        os.rmdir(dirname)
37    except OSError:
38        pass
39
40class PosixPathTest(unittest.TestCase):
41
42    def setUp(self):
43        self.tearDown()
44
45    def tearDown(self):
46        for suffix in ["", "1", "2"]:
47            os_helper.unlink(os_helper.TESTFN + suffix)
48            safe_rmdir(os_helper.TESTFN + suffix)
49
50    def test_join(self):
51        fn = posixpath.join
52        self.assertEqual(fn("/foo", "bar", "/bar", "baz"), "/bar/baz")
53        self.assertEqual(fn("/foo", "bar", "baz"),         "/foo/bar/baz")
54        self.assertEqual(fn("/foo/", "bar/", "baz/"),      "/foo/bar/baz/")
55
56        self.assertEqual(fn(b"/foo", b"bar", b"/bar", b"baz"), b"/bar/baz")
57        self.assertEqual(fn(b"/foo", b"bar", b"baz"),          b"/foo/bar/baz")
58        self.assertEqual(fn(b"/foo/", b"bar/", b"baz/"),       b"/foo/bar/baz/")
59
60        self.assertEqual(fn("a", ""),          "a/")
61        self.assertEqual(fn("a", "", ""),      "a/")
62        self.assertEqual(fn("a", "b"),         "a/b")
63        self.assertEqual(fn("a", "b/"),        "a/b/")
64        self.assertEqual(fn("a/", "b"),        "a/b")
65        self.assertEqual(fn("a/", "b/"),       "a/b/")
66        self.assertEqual(fn("a", "b/c", "d"),  "a/b/c/d")
67        self.assertEqual(fn("a", "b//c", "d"), "a/b//c/d")
68        self.assertEqual(fn("a", "b/c/", "d"), "a/b/c/d")
69        self.assertEqual(fn("/a", "b"),        "/a/b")
70        self.assertEqual(fn("/a/", "b"),       "/a/b")
71        self.assertEqual(fn("a", "/b", "c"),   "/b/c")
72        self.assertEqual(fn("a", "/b", "/c"),  "/c")
73
74    def test_split(self):
75        self.assertEqual(posixpath.split("/foo/bar"), ("/foo", "bar"))
76        self.assertEqual(posixpath.split("/"), ("/", ""))
77        self.assertEqual(posixpath.split("foo"), ("", "foo"))
78        self.assertEqual(posixpath.split("////foo"), ("////", "foo"))
79        self.assertEqual(posixpath.split("//foo//bar"), ("//foo", "bar"))
80
81        self.assertEqual(posixpath.split(b"/foo/bar"), (b"/foo", b"bar"))
82        self.assertEqual(posixpath.split(b"/"), (b"/", b""))
83        self.assertEqual(posixpath.split(b"foo"), (b"", b"foo"))
84        self.assertEqual(posixpath.split(b"////foo"), (b"////", b"foo"))
85        self.assertEqual(posixpath.split(b"//foo//bar"), (b"//foo", b"bar"))
86
87    def splitextTest(self, path, filename, ext):
88        self.assertEqual(posixpath.splitext(path), (filename, ext))
89        self.assertEqual(posixpath.splitext("/" + path), ("/" + filename, ext))
90        self.assertEqual(posixpath.splitext("abc/" + path),
91                         ("abc/" + filename, ext))
92        self.assertEqual(posixpath.splitext("abc.def/" + path),
93                         ("abc.def/" + filename, ext))
94        self.assertEqual(posixpath.splitext("/abc.def/" + path),
95                         ("/abc.def/" + filename, ext))
96        self.assertEqual(posixpath.splitext(path + "/"),
97                         (filename + ext + "/", ""))
98
99        path = bytes(path, "ASCII")
100        filename = bytes(filename, "ASCII")
101        ext = bytes(ext, "ASCII")
102
103        self.assertEqual(posixpath.splitext(path), (filename, ext))
104        self.assertEqual(posixpath.splitext(b"/" + path),
105                         (b"/" + filename, ext))
106        self.assertEqual(posixpath.splitext(b"abc/" + path),
107                         (b"abc/" + filename, ext))
108        self.assertEqual(posixpath.splitext(b"abc.def/" + path),
109                         (b"abc.def/" + filename, ext))
110        self.assertEqual(posixpath.splitext(b"/abc.def/" + path),
111                         (b"/abc.def/" + filename, ext))
112        self.assertEqual(posixpath.splitext(path + b"/"),
113                         (filename + ext + b"/", b""))
114
115    def test_splitext(self):
116        self.splitextTest("foo.bar", "foo", ".bar")
117        self.splitextTest("foo.boo.bar", "foo.boo", ".bar")
118        self.splitextTest("foo.boo.biff.bar", "foo.boo.biff", ".bar")
119        self.splitextTest(".csh.rc", ".csh", ".rc")
120        self.splitextTest("nodots", "nodots", "")
121        self.splitextTest(".cshrc", ".cshrc", "")
122        self.splitextTest("...manydots", "...manydots", "")
123        self.splitextTest("...manydots.ext", "...manydots", ".ext")
124        self.splitextTest(".", ".", "")
125        self.splitextTest("..", "..", "")
126        self.splitextTest("........", "........", "")
127        self.splitextTest("", "", "")
128
129    def test_splitroot(self):
130        f = posixpath.splitroot
131        self.assertEqual(f(''), ('', '', ''))
132        self.assertEqual(f('a'), ('', '', 'a'))
133        self.assertEqual(f('a/b'), ('', '', 'a/b'))
134        self.assertEqual(f('a/b/'), ('', '', 'a/b/'))
135        self.assertEqual(f('/a'), ('', '/', 'a'))
136        self.assertEqual(f('/a/b'), ('', '/', 'a/b'))
137        self.assertEqual(f('/a/b/'), ('', '/', 'a/b/'))
138        # The root is collapsed when there are redundant slashes
139        # except when there are exactly two leading slashes, which
140        # is a special case in POSIX.
141        self.assertEqual(f('//a'), ('', '//', 'a'))
142        self.assertEqual(f('///a'), ('', '/', '//a'))
143        self.assertEqual(f('///a/b'), ('', '/', '//a/b'))
144        # Paths which look like NT paths aren't treated specially.
145        self.assertEqual(f('c:/a/b'), ('', '', 'c:/a/b'))
146        self.assertEqual(f('\\/a/b'), ('', '', '\\/a/b'))
147        self.assertEqual(f('\\a\\b'), ('', '', '\\a\\b'))
148        # Byte paths are supported
149        self.assertEqual(f(b''), (b'', b'', b''))
150        self.assertEqual(f(b'a'), (b'', b'', b'a'))
151        self.assertEqual(f(b'/a'), (b'', b'/', b'a'))
152        self.assertEqual(f(b'//a'), (b'', b'//', b'a'))
153        self.assertEqual(f(b'///a'), (b'', b'/', b'//a'))
154
155    def test_isabs(self):
156        self.assertIs(posixpath.isabs(""), False)
157        self.assertIs(posixpath.isabs("/"), True)
158        self.assertIs(posixpath.isabs("/foo"), True)
159        self.assertIs(posixpath.isabs("/foo/bar"), True)
160        self.assertIs(posixpath.isabs("foo/bar"), False)
161
162        self.assertIs(posixpath.isabs(b""), False)
163        self.assertIs(posixpath.isabs(b"/"), True)
164        self.assertIs(posixpath.isabs(b"/foo"), True)
165        self.assertIs(posixpath.isabs(b"/foo/bar"), True)
166        self.assertIs(posixpath.isabs(b"foo/bar"), False)
167
168    def test_basename(self):
169        self.assertEqual(posixpath.basename("/foo/bar"), "bar")
170        self.assertEqual(posixpath.basename("/"), "")
171        self.assertEqual(posixpath.basename("foo"), "foo")
172        self.assertEqual(posixpath.basename("////foo"), "foo")
173        self.assertEqual(posixpath.basename("//foo//bar"), "bar")
174
175        self.assertEqual(posixpath.basename(b"/foo/bar"), b"bar")
176        self.assertEqual(posixpath.basename(b"/"), b"")
177        self.assertEqual(posixpath.basename(b"foo"), b"foo")
178        self.assertEqual(posixpath.basename(b"////foo"), b"foo")
179        self.assertEqual(posixpath.basename(b"//foo//bar"), b"bar")
180
181    def test_dirname(self):
182        self.assertEqual(posixpath.dirname("/foo/bar"), "/foo")
183        self.assertEqual(posixpath.dirname("/"), "/")
184        self.assertEqual(posixpath.dirname("foo"), "")
185        self.assertEqual(posixpath.dirname("////foo"), "////")
186        self.assertEqual(posixpath.dirname("//foo//bar"), "//foo")
187
188        self.assertEqual(posixpath.dirname(b"/foo/bar"), b"/foo")
189        self.assertEqual(posixpath.dirname(b"/"), b"/")
190        self.assertEqual(posixpath.dirname(b"foo"), b"")
191        self.assertEqual(posixpath.dirname(b"////foo"), b"////")
192        self.assertEqual(posixpath.dirname(b"//foo//bar"), b"//foo")
193
194    def test_islink(self):
195        self.assertIs(posixpath.islink(os_helper.TESTFN + "1"), False)
196        self.assertIs(posixpath.lexists(os_helper.TESTFN + "2"), False)
197
198        with open(os_helper.TESTFN + "1", "wb") as f:
199            f.write(b"foo")
200        self.assertIs(posixpath.islink(os_helper.TESTFN + "1"), False)
201
202        if os_helper.can_symlink():
203            os.symlink(os_helper.TESTFN + "1", os_helper.TESTFN + "2")
204            self.assertIs(posixpath.islink(os_helper.TESTFN + "2"), True)
205            os.remove(os_helper.TESTFN + "1")
206            self.assertIs(posixpath.islink(os_helper.TESTFN + "2"), True)
207            self.assertIs(posixpath.exists(os_helper.TESTFN + "2"), False)
208            self.assertIs(posixpath.lexists(os_helper.TESTFN + "2"), True)
209
210        self.assertIs(posixpath.islink(os_helper.TESTFN + "\udfff"), False)
211        self.assertIs(posixpath.islink(os.fsencode(os_helper.TESTFN) + b"\xff"), False)
212        self.assertIs(posixpath.islink(os_helper.TESTFN + "\x00"), False)
213        self.assertIs(posixpath.islink(os.fsencode(os_helper.TESTFN) + b"\x00"), False)
214
215    def test_ismount(self):
216        self.assertIs(posixpath.ismount("/"), True)
217        self.assertIs(posixpath.ismount(b"/"), True)
218        self.assertIs(posixpath.ismount(FakePath("/")), True)
219        self.assertIs(posixpath.ismount(FakePath(b"/")), True)
220
221    def test_ismount_non_existent(self):
222        # Non-existent mountpoint.
223        self.assertIs(posixpath.ismount(ABSTFN), False)
224        try:
225            os.mkdir(ABSTFN)
226            self.assertIs(posixpath.ismount(ABSTFN), False)
227        finally:
228            safe_rmdir(ABSTFN)
229
230        self.assertIs(posixpath.ismount('/\udfff'), False)
231        self.assertIs(posixpath.ismount(b'/\xff'), False)
232        self.assertIs(posixpath.ismount('/\x00'), False)
233        self.assertIs(posixpath.ismount(b'/\x00'), False)
234
235    @os_helper.skip_unless_symlink
236    def test_ismount_symlinks(self):
237        # Symlinks are never mountpoints.
238        try:
239            os.symlink("/", ABSTFN)
240            self.assertIs(posixpath.ismount(ABSTFN), False)
241        finally:
242            os.unlink(ABSTFN)
243
244    @unittest.skipIf(posix is None, "Test requires posix module")
245    def test_ismount_different_device(self):
246        # Simulate the path being on a different device from its parent by
247        # mocking out st_dev.
248        save_lstat = os.lstat
249        def fake_lstat(path):
250            st_ino = 0
251            st_dev = 0
252            if path == ABSTFN:
253                st_dev = 1
254                st_ino = 1
255            return posix.stat_result((0, st_ino, st_dev, 0, 0, 0, 0, 0, 0, 0))
256        try:
257            os.lstat = fake_lstat
258            self.assertIs(posixpath.ismount(ABSTFN), True)
259        finally:
260            os.lstat = save_lstat
261
262    @unittest.skipIf(posix is None, "Test requires posix module")
263    def test_ismount_directory_not_readable(self):
264        # issue #2466: Simulate ismount run on a directory that is not
265        # readable, which used to return False.
266        save_lstat = os.lstat
267        def fake_lstat(path):
268            st_ino = 0
269            st_dev = 0
270            if path.startswith(ABSTFN) and path != ABSTFN:
271                # ismount tries to read something inside the ABSTFN directory;
272                # simulate this being forbidden (no read permission).
273                raise OSError("Fake [Errno 13] Permission denied")
274            if path == ABSTFN:
275                st_dev = 1
276                st_ino = 1
277            return posix.stat_result((0, st_ino, st_dev, 0, 0, 0, 0, 0, 0, 0))
278        try:
279            os.lstat = fake_lstat
280            self.assertIs(posixpath.ismount(ABSTFN), True)
281        finally:
282            os.lstat = save_lstat
283
284    def test_isjunction(self):
285        self.assertFalse(posixpath.isjunction(ABSTFN))
286
287    @unittest.skipIf(sys.platform == 'win32', "Fast paths are not for win32")
288    @cpython_only
289    def test_fast_paths_in_use(self):
290        # There are fast paths of these functions implemented in posixmodule.c.
291        # Confirm that they are being used, and not the Python fallbacks
292        self.assertTrue(os.path.splitroot is posix._path_splitroot_ex)
293        self.assertFalse(inspect.isfunction(os.path.splitroot))
294        self.assertTrue(os.path.normpath is posix._path_normpath)
295        self.assertFalse(inspect.isfunction(os.path.normpath))
296
297    def test_expanduser(self):
298        self.assertEqual(posixpath.expanduser("foo"), "foo")
299        self.assertEqual(posixpath.expanduser(b"foo"), b"foo")
300
301    def test_expanduser_home_envvar(self):
302        with os_helper.EnvironmentVarGuard() as env:
303            env['HOME'] = '/home/victor'
304            self.assertEqual(posixpath.expanduser("~"), "/home/victor")
305
306            # expanduser() strips trailing slash
307            env['HOME'] = '/home/victor/'
308            self.assertEqual(posixpath.expanduser("~"), "/home/victor")
309
310            for home in '/', '', '//', '///':
311                with self.subTest(home=home):
312                    env['HOME'] = home
313                    self.assertEqual(posixpath.expanduser("~"), "/")
314                    self.assertEqual(posixpath.expanduser("~/"), "/")
315                    self.assertEqual(posixpath.expanduser("~/foo"), "/foo")
316
317    @unittest.skipIf(sys.platform == "vxworks",
318                     "no home directory on VxWorks")
319    def test_expanduser_pwd(self):
320        pwd = import_helper.import_module('pwd')
321
322        self.assertIsInstance(posixpath.expanduser("~/"), str)
323        self.assertIsInstance(posixpath.expanduser(b"~/"), bytes)
324
325        # if home directory == root directory, this test makes no sense
326        if posixpath.expanduser("~") != '/':
327            self.assertEqual(
328                posixpath.expanduser("~") + "/",
329                posixpath.expanduser("~/")
330            )
331            self.assertEqual(
332                posixpath.expanduser(b"~") + b"/",
333                posixpath.expanduser(b"~/")
334            )
335        self.assertIsInstance(posixpath.expanduser("~root/"), str)
336        self.assertIsInstance(posixpath.expanduser("~foo/"), str)
337        self.assertIsInstance(posixpath.expanduser(b"~root/"), bytes)
338        self.assertIsInstance(posixpath.expanduser(b"~foo/"), bytes)
339
340        with os_helper.EnvironmentVarGuard() as env:
341            # expanduser should fall back to using the password database
342            del env['HOME']
343
344            home = pwd.getpwuid(os.getuid()).pw_dir
345            # $HOME can end with a trailing /, so strip it (see #17809)
346            home = home.rstrip("/") or '/'
347            self.assertEqual(posixpath.expanduser("~"), home)
348
349            # bpo-10496: If the HOME environment variable is not set and the
350            # user (current identifier or name in the path) doesn't exist in
351            # the password database (pwd.getuid() or pwd.getpwnam() fail),
352            # expanduser() must return the path unchanged.
353            with mock.patch.object(pwd, 'getpwuid', side_effect=KeyError), \
354                 mock.patch.object(pwd, 'getpwnam', side_effect=KeyError):
355                for path in ('~', '~/.local', '~vstinner/'):
356                    self.assertEqual(posixpath.expanduser(path), path)
357
358    @unittest.skipIf(sys.platform == "vxworks",
359                     "no home directory on VxWorks")
360    def test_expanduser_pwd2(self):
361        pwd = import_helper.import_module('pwd')
362        for all_entry in get_attribute(pwd, 'getpwall')():
363            name = all_entry.pw_name
364
365            # gh-121200: pw_dir can be different between getpwall() and
366            # getpwnam(), so use getpwnam() pw_dir as expanduser() does.
367            entry = pwd.getpwnam(name)
368            home = entry.pw_dir
369            home = home.rstrip('/') or '/'
370
371            with self.subTest(all_entry=all_entry, entry=entry):
372                self.assertEqual(posixpath.expanduser('~' + name), home)
373                self.assertEqual(posixpath.expanduser(os.fsencode('~' + name)),
374                                 os.fsencode(home))
375
376    NORMPATH_CASES = [
377        ("", "."),
378        ("/", "/"),
379        ("/.", "/"),
380        ("/./", "/"),
381        ("/.//.", "/"),
382        ("/./foo/bar", "/foo/bar"),
383        ("/foo", "/foo"),
384        ("/foo/bar", "/foo/bar"),
385        ("//", "//"),
386        ("///", "/"),
387        ("///foo/.//bar//", "/foo/bar"),
388        ("///foo/.//bar//.//..//.//baz///", "/foo/baz"),
389        ("///..//./foo/.//bar", "/foo/bar"),
390        (".", "."),
391        (".//.", "."),
392        ("./foo/bar", "foo/bar"),
393        ("..", ".."),
394        ("../", ".."),
395        ("../foo", "../foo"),
396        ("../../foo", "../../foo"),
397        ("../foo/../bar", "../bar"),
398        ("../../foo/../bar/./baz/boom/..", "../../bar/baz"),
399        ("/..", "/"),
400        ("/..", "/"),
401        ("/../", "/"),
402        ("/..//", "/"),
403        ("//.", "//"),
404        ("//..", "//"),
405        ("//...", "//..."),
406        ("//../foo", "//foo"),
407        ("//../../foo", "//foo"),
408        ("/../foo", "/foo"),
409        ("/../../foo", "/foo"),
410        ("/../foo/../", "/"),
411        ("/../foo/../bar", "/bar"),
412        ("/../../foo/../bar/./baz/boom/..", "/bar/baz"),
413        ("/../../foo/../bar/./baz/boom/.", "/bar/baz/boom"),
414        ("foo/../bar/baz", "bar/baz"),
415        ("foo/../../bar/baz", "../bar/baz"),
416        ("foo/../../../bar/baz", "../../bar/baz"),
417        ("foo///../bar/.././../baz/boom", "../baz/boom"),
418        ("foo/bar/../..///../../baz/boom", "../../baz/boom"),
419        ("/foo/..", "/"),
420        ("/foo/../..", "/"),
421        ("//foo/..", "//"),
422        ("//foo/../..", "//"),
423        ("///foo/..", "/"),
424        ("///foo/../..", "/"),
425        ("////foo/..", "/"),
426        ("/////foo/..", "/"),
427    ]
428
429    def test_normpath(self):
430        for path, expected in self.NORMPATH_CASES:
431            with self.subTest(path):
432                result = posixpath.normpath(path)
433                self.assertEqual(result, expected)
434
435            path = path.encode('utf-8')
436            expected = expected.encode('utf-8')
437            with self.subTest(path, type=bytes):
438                result = posixpath.normpath(path)
439                self.assertEqual(result, expected)
440
441    @skip_if_ABSTFN_contains_backslash
442    def test_realpath_curdir(self):
443        self.assertEqual(realpath('.'), os.getcwd())
444        self.assertEqual(realpath('./.'), os.getcwd())
445        self.assertEqual(realpath('/'.join(['.'] * 100)), os.getcwd())
446
447        self.assertEqual(realpath(b'.'), os.getcwdb())
448        self.assertEqual(realpath(b'./.'), os.getcwdb())
449        self.assertEqual(realpath(b'/'.join([b'.'] * 100)), os.getcwdb())
450
451    @skip_if_ABSTFN_contains_backslash
452    def test_realpath_pardir(self):
453        self.assertEqual(realpath('..'), dirname(os.getcwd()))
454        self.assertEqual(realpath('../..'), dirname(dirname(os.getcwd())))
455        self.assertEqual(realpath('/'.join(['..'] * 100)), '/')
456
457        self.assertEqual(realpath(b'..'), dirname(os.getcwdb()))
458        self.assertEqual(realpath(b'../..'), dirname(dirname(os.getcwdb())))
459        self.assertEqual(realpath(b'/'.join([b'..'] * 100)), b'/')
460
461    @os_helper.skip_unless_symlink
462    @skip_if_ABSTFN_contains_backslash
463    def test_realpath_basic(self):
464        # Basic operation.
465        try:
466            os.symlink(ABSTFN+"1", ABSTFN)
467            self.assertEqual(realpath(ABSTFN), ABSTFN+"1")
468        finally:
469            os_helper.unlink(ABSTFN)
470
471    @os_helper.skip_unless_symlink
472    @skip_if_ABSTFN_contains_backslash
473    def test_realpath_strict(self):
474        # Bug #43757: raise FileNotFoundError in strict mode if we encounter
475        # a path that does not exist.
476        try:
477            os.symlink(ABSTFN+"1", ABSTFN)
478            self.assertRaises(FileNotFoundError, realpath, ABSTFN, strict=True)
479            self.assertRaises(FileNotFoundError, realpath, ABSTFN + "2", strict=True)
480        finally:
481            os_helper.unlink(ABSTFN)
482
483    @os_helper.skip_unless_symlink
484    @skip_if_ABSTFN_contains_backslash
485    def test_realpath_relative(self):
486        try:
487            os.symlink(posixpath.relpath(ABSTFN+"1"), ABSTFN)
488            self.assertEqual(realpath(ABSTFN), ABSTFN+"1")
489        finally:
490            os_helper.unlink(ABSTFN)
491
492    @os_helper.skip_unless_symlink
493    @skip_if_ABSTFN_contains_backslash
494    def test_realpath_missing_pardir(self):
495        try:
496            os.symlink(os_helper.TESTFN + "1", os_helper.TESTFN)
497            self.assertEqual(realpath("nonexistent/../" + os_helper.TESTFN), ABSTFN + "1")
498        finally:
499            os_helper.unlink(os_helper.TESTFN)
500
501    @os_helper.skip_unless_symlink
502    @skip_if_ABSTFN_contains_backslash
503    def test_realpath_symlink_loops(self):
504        # Bug #930024, return the path unchanged if we get into an infinite
505        # symlink loop in non-strict mode (default).
506        try:
507            os.symlink(ABSTFN, ABSTFN)
508            self.assertEqual(realpath(ABSTFN), ABSTFN)
509
510            os.symlink(ABSTFN+"1", ABSTFN+"2")
511            os.symlink(ABSTFN+"2", ABSTFN+"1")
512            self.assertEqual(realpath(ABSTFN+"1"), ABSTFN+"1")
513            self.assertEqual(realpath(ABSTFN+"2"), ABSTFN+"2")
514
515            self.assertEqual(realpath(ABSTFN+"1/x"), ABSTFN+"1/x")
516            self.assertEqual(realpath(ABSTFN+"1/.."), dirname(ABSTFN))
517            self.assertEqual(realpath(ABSTFN+"1/../x"), dirname(ABSTFN) + "/x")
518            os.symlink(ABSTFN+"x", ABSTFN+"y")
519            self.assertEqual(realpath(ABSTFN+"1/../" + basename(ABSTFN) + "y"),
520                             ABSTFN + "x")
521            self.assertEqual(realpath(ABSTFN+"1/../" + basename(ABSTFN) + "1"),
522                             ABSTFN + "1")
523
524            os.symlink(basename(ABSTFN) + "a/b", ABSTFN+"a")
525            self.assertEqual(realpath(ABSTFN+"a"), ABSTFN+"a/b")
526
527            os.symlink("../" + basename(dirname(ABSTFN)) + "/" +
528                       basename(ABSTFN) + "c", ABSTFN+"c")
529            self.assertEqual(realpath(ABSTFN+"c"), ABSTFN+"c")
530
531            # Test using relative path as well.
532            with os_helper.change_cwd(dirname(ABSTFN)):
533                self.assertEqual(realpath(basename(ABSTFN)), ABSTFN)
534        finally:
535            os_helper.unlink(ABSTFN)
536            os_helper.unlink(ABSTFN+"1")
537            os_helper.unlink(ABSTFN+"2")
538            os_helper.unlink(ABSTFN+"y")
539            os_helper.unlink(ABSTFN+"c")
540            os_helper.unlink(ABSTFN+"a")
541
542    @os_helper.skip_unless_symlink
543    @skip_if_ABSTFN_contains_backslash
544    def test_realpath_symlink_loops_strict(self):
545        # Bug #43757, raise OSError if we get into an infinite symlink loop in
546        # strict mode.
547        try:
548            os.symlink(ABSTFN, ABSTFN)
549            self.assertRaises(OSError, realpath, ABSTFN, strict=True)
550
551            os.symlink(ABSTFN+"1", ABSTFN+"2")
552            os.symlink(ABSTFN+"2", ABSTFN+"1")
553            self.assertRaises(OSError, realpath, ABSTFN+"1", strict=True)
554            self.assertRaises(OSError, realpath, ABSTFN+"2", strict=True)
555
556            self.assertRaises(OSError, realpath, ABSTFN+"1/x", strict=True)
557            self.assertRaises(OSError, realpath, ABSTFN+"1/..", strict=True)
558            self.assertRaises(OSError, realpath, ABSTFN+"1/../x", strict=True)
559            os.symlink(ABSTFN+"x", ABSTFN+"y")
560            self.assertRaises(OSError, realpath,
561                              ABSTFN+"1/../" + basename(ABSTFN) + "y", strict=True)
562            self.assertRaises(OSError, realpath,
563                              ABSTFN+"1/../" + basename(ABSTFN) + "1", strict=True)
564
565            os.symlink(basename(ABSTFN) + "a/b", ABSTFN+"a")
566            self.assertRaises(OSError, realpath, ABSTFN+"a", strict=True)
567
568            os.symlink("../" + basename(dirname(ABSTFN)) + "/" +
569                       basename(ABSTFN) + "c", ABSTFN+"c")
570            self.assertRaises(OSError, realpath, ABSTFN+"c", strict=True)
571
572            # Test using relative path as well.
573            with os_helper.change_cwd(dirname(ABSTFN)):
574                self.assertRaises(OSError, realpath, basename(ABSTFN), strict=True)
575        finally:
576            os_helper.unlink(ABSTFN)
577            os_helper.unlink(ABSTFN+"1")
578            os_helper.unlink(ABSTFN+"2")
579            os_helper.unlink(ABSTFN+"y")
580            os_helper.unlink(ABSTFN+"c")
581            os_helper.unlink(ABSTFN+"a")
582
583    @os_helper.skip_unless_symlink
584    @skip_if_ABSTFN_contains_backslash
585    def test_realpath_repeated_indirect_symlinks(self):
586        # Issue #6975.
587        try:
588            os.mkdir(ABSTFN)
589            os.symlink('../' + basename(ABSTFN), ABSTFN + '/self')
590            os.symlink('self/self/self', ABSTFN + '/link')
591            self.assertEqual(realpath(ABSTFN + '/link'), ABSTFN)
592        finally:
593            os_helper.unlink(ABSTFN + '/self')
594            os_helper.unlink(ABSTFN + '/link')
595            safe_rmdir(ABSTFN)
596
597    @os_helper.skip_unless_symlink
598    @skip_if_ABSTFN_contains_backslash
599    def test_realpath_deep_recursion(self):
600        depth = 10
601        try:
602            os.mkdir(ABSTFN)
603            for i in range(depth):
604                os.symlink('/'.join(['%d' % i] * 10), ABSTFN + '/%d' % (i + 1))
605            os.symlink('.', ABSTFN + '/0')
606            self.assertEqual(realpath(ABSTFN + '/%d' % depth), ABSTFN)
607
608            # Test using relative path as well.
609            with os_helper.change_cwd(ABSTFN):
610                self.assertEqual(realpath('%d' % depth), ABSTFN)
611        finally:
612            for i in range(depth + 1):
613                os_helper.unlink(ABSTFN + '/%d' % i)
614            safe_rmdir(ABSTFN)
615
616    @os_helper.skip_unless_symlink
617    @skip_if_ABSTFN_contains_backslash
618    def test_realpath_resolve_parents(self):
619        # We also need to resolve any symlinks in the parents of a relative
620        # path passed to realpath. E.g.: current working directory is
621        # /usr/doc with 'doc' being a symlink to /usr/share/doc. We call
622        # realpath("a"). This should return /usr/share/doc/a/.
623        try:
624            os.mkdir(ABSTFN)
625            os.mkdir(ABSTFN + "/y")
626            os.symlink(ABSTFN + "/y", ABSTFN + "/k")
627
628            with os_helper.change_cwd(ABSTFN + "/k"):
629                self.assertEqual(realpath("a"), ABSTFN + "/y/a")
630        finally:
631            os_helper.unlink(ABSTFN + "/k")
632            safe_rmdir(ABSTFN + "/y")
633            safe_rmdir(ABSTFN)
634
635    @os_helper.skip_unless_symlink
636    @skip_if_ABSTFN_contains_backslash
637    def test_realpath_resolve_before_normalizing(self):
638        # Bug #990669: Symbolic links should be resolved before we
639        # normalize the path. E.g.: if we have directories 'a', 'k' and 'y'
640        # in the following hierarchy:
641        # a/k/y
642        #
643        # and a symbolic link 'link-y' pointing to 'y' in directory 'a',
644        # then realpath("link-y/..") should return 'k', not 'a'.
645        try:
646            os.mkdir(ABSTFN)
647            os.mkdir(ABSTFN + "/k")
648            os.mkdir(ABSTFN + "/k/y")
649            os.symlink(ABSTFN + "/k/y", ABSTFN + "/link-y")
650
651            # Absolute path.
652            self.assertEqual(realpath(ABSTFN + "/link-y/.."), ABSTFN + "/k")
653            # Relative path.
654            with os_helper.change_cwd(dirname(ABSTFN)):
655                self.assertEqual(realpath(basename(ABSTFN) + "/link-y/.."),
656                                 ABSTFN + "/k")
657        finally:
658            os_helper.unlink(ABSTFN + "/link-y")
659            safe_rmdir(ABSTFN + "/k/y")
660            safe_rmdir(ABSTFN + "/k")
661            safe_rmdir(ABSTFN)
662
663    @os_helper.skip_unless_symlink
664    @skip_if_ABSTFN_contains_backslash
665    def test_realpath_resolve_first(self):
666        # Bug #1213894: The first component of the path, if not absolute,
667        # must be resolved too.
668
669        try:
670            os.mkdir(ABSTFN)
671            os.mkdir(ABSTFN + "/k")
672            os.symlink(ABSTFN, ABSTFN + "link")
673            with os_helper.change_cwd(dirname(ABSTFN)):
674                base = basename(ABSTFN)
675                self.assertEqual(realpath(base + "link"), ABSTFN)
676                self.assertEqual(realpath(base + "link/k"), ABSTFN + "/k")
677        finally:
678            os_helper.unlink(ABSTFN + "link")
679            safe_rmdir(ABSTFN + "/k")
680            safe_rmdir(ABSTFN)
681
682    @os_helper.skip_unless_symlink
683    @skip_if_ABSTFN_contains_backslash
684    @unittest.skipIf(os.chmod not in os.supports_follow_symlinks, "Can't set symlink permissions")
685    @unittest.skipIf(sys.platform != "darwin", "only macOS requires read permission to readlink()")
686    def test_realpath_unreadable_symlink(self):
687        try:
688            os.symlink(ABSTFN+"1", ABSTFN)
689            os.chmod(ABSTFN, 0o000, follow_symlinks=False)
690            self.assertEqual(realpath(ABSTFN), ABSTFN)
691            self.assertEqual(realpath(ABSTFN + '/foo'), ABSTFN + '/foo')
692            self.assertEqual(realpath(ABSTFN + '/../foo'), dirname(ABSTFN) + '/foo')
693            self.assertEqual(realpath(ABSTFN + '/foo/..'), ABSTFN)
694            with self.assertRaises(PermissionError):
695                realpath(ABSTFN, strict=True)
696        finally:
697            os.chmod(ABSTFN, 0o755, follow_symlinks=False)
698            os.unlink(ABSTFN)
699
700    @skip_if_ABSTFN_contains_backslash
701    def test_realpath_nonterminal_file(self):
702        try:
703            with open(ABSTFN, 'w') as f:
704                f.write('test_posixpath wuz ere')
705            self.assertEqual(realpath(ABSTFN, strict=False), ABSTFN)
706            self.assertEqual(realpath(ABSTFN, strict=True), ABSTFN)
707            self.assertEqual(realpath(ABSTFN + "/", strict=False), ABSTFN)
708            self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/", strict=True)
709            self.assertEqual(realpath(ABSTFN + "/.", strict=False), ABSTFN)
710            self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/.", strict=True)
711            self.assertEqual(realpath(ABSTFN + "/..", strict=False), dirname(ABSTFN))
712            self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/..", strict=True)
713            self.assertEqual(realpath(ABSTFN + "/subdir", strict=False), ABSTFN + "/subdir")
714            self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/subdir", strict=True)
715        finally:
716            os_helper.unlink(ABSTFN)
717
718    @os_helper.skip_unless_symlink
719    @skip_if_ABSTFN_contains_backslash
720    def test_realpath_nonterminal_symlink_to_file(self):
721        try:
722            with open(ABSTFN + "1", 'w') as f:
723                f.write('test_posixpath wuz ere')
724            os.symlink(ABSTFN + "1", ABSTFN)
725            self.assertEqual(realpath(ABSTFN, strict=False), ABSTFN + "1")
726            self.assertEqual(realpath(ABSTFN, strict=True), ABSTFN + "1")
727            self.assertEqual(realpath(ABSTFN + "/", strict=False), ABSTFN + "1")
728            self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/", strict=True)
729            self.assertEqual(realpath(ABSTFN + "/.", strict=False), ABSTFN + "1")
730            self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/.", strict=True)
731            self.assertEqual(realpath(ABSTFN + "/..", strict=False), dirname(ABSTFN))
732            self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/..", strict=True)
733            self.assertEqual(realpath(ABSTFN + "/subdir", strict=False), ABSTFN + "1/subdir")
734            self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/subdir", strict=True)
735        finally:
736            os_helper.unlink(ABSTFN)
737
738    @os_helper.skip_unless_symlink
739    @skip_if_ABSTFN_contains_backslash
740    def test_realpath_nonterminal_symlink_to_symlinks_to_file(self):
741        try:
742            with open(ABSTFN + "2", 'w') as f:
743                f.write('test_posixpath wuz ere')
744            os.symlink(ABSTFN + "2", ABSTFN + "1")
745            os.symlink(ABSTFN + "1", ABSTFN)
746            self.assertEqual(realpath(ABSTFN, strict=False), ABSTFN + "2")
747            self.assertEqual(realpath(ABSTFN, strict=True), ABSTFN + "2")
748            self.assertEqual(realpath(ABSTFN + "/", strict=False), ABSTFN + "2")
749            self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/", strict=True)
750            self.assertEqual(realpath(ABSTFN + "/.", strict=False), ABSTFN + "2")
751            self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/.", strict=True)
752            self.assertEqual(realpath(ABSTFN + "/..", strict=False), dirname(ABSTFN))
753            self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/..", strict=True)
754            self.assertEqual(realpath(ABSTFN + "/subdir", strict=False), ABSTFN + "2/subdir")
755            self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/subdir", strict=True)
756        finally:
757            os_helper.unlink(ABSTFN)
758
759    def test_relpath(self):
760        (real_getcwd, os.getcwd) = (os.getcwd, lambda: r"/home/user/bar")
761        try:
762            curdir = os.path.split(os.getcwd())[-1]
763            self.assertRaises(TypeError, posixpath.relpath, None)
764            self.assertRaises(ValueError, posixpath.relpath, "")
765            self.assertEqual(posixpath.relpath("a"), "a")
766            self.assertEqual(posixpath.relpath(posixpath.abspath("a")), "a")
767            self.assertEqual(posixpath.relpath("a/b"), "a/b")
768            self.assertEqual(posixpath.relpath("../a/b"), "../a/b")
769            self.assertEqual(posixpath.relpath("a", "../b"), "../"+curdir+"/a")
770            self.assertEqual(posixpath.relpath("a/b", "../c"),
771                             "../"+curdir+"/a/b")
772            self.assertEqual(posixpath.relpath("a", "b/c"), "../../a")
773            self.assertEqual(posixpath.relpath("a", "a"), ".")
774            self.assertEqual(posixpath.relpath("/foo/bar/bat", "/x/y/z"), '../../../foo/bar/bat')
775            self.assertEqual(posixpath.relpath("/foo/bar/bat", "/foo/bar"), 'bat')
776            self.assertEqual(posixpath.relpath("/foo/bar/bat", "/"), 'foo/bar/bat')
777            self.assertEqual(posixpath.relpath("/", "/foo/bar/bat"), '../../..')
778            self.assertEqual(posixpath.relpath("/foo/bar/bat", "/x"), '../foo/bar/bat')
779            self.assertEqual(posixpath.relpath("/x", "/foo/bar/bat"), '../../../x')
780            self.assertEqual(posixpath.relpath("/", "/"), '.')
781            self.assertEqual(posixpath.relpath("/a", "/a"), '.')
782            self.assertEqual(posixpath.relpath("/a/b", "/a/b"), '.')
783        finally:
784            os.getcwd = real_getcwd
785
786    def test_relpath_bytes(self):
787        (real_getcwdb, os.getcwdb) = (os.getcwdb, lambda: br"/home/user/bar")
788        try:
789            curdir = os.path.split(os.getcwdb())[-1]
790            self.assertRaises(ValueError, posixpath.relpath, b"")
791            self.assertEqual(posixpath.relpath(b"a"), b"a")
792            self.assertEqual(posixpath.relpath(posixpath.abspath(b"a")), b"a")
793            self.assertEqual(posixpath.relpath(b"a/b"), b"a/b")
794            self.assertEqual(posixpath.relpath(b"../a/b"), b"../a/b")
795            self.assertEqual(posixpath.relpath(b"a", b"../b"),
796                             b"../"+curdir+b"/a")
797            self.assertEqual(posixpath.relpath(b"a/b", b"../c"),
798                             b"../"+curdir+b"/a/b")
799            self.assertEqual(posixpath.relpath(b"a", b"b/c"), b"../../a")
800            self.assertEqual(posixpath.relpath(b"a", b"a"), b".")
801            self.assertEqual(posixpath.relpath(b"/foo/bar/bat", b"/x/y/z"), b'../../../foo/bar/bat')
802            self.assertEqual(posixpath.relpath(b"/foo/bar/bat", b"/foo/bar"), b'bat')
803            self.assertEqual(posixpath.relpath(b"/foo/bar/bat", b"/"), b'foo/bar/bat')
804            self.assertEqual(posixpath.relpath(b"/", b"/foo/bar/bat"), b'../../..')
805            self.assertEqual(posixpath.relpath(b"/foo/bar/bat", b"/x"), b'../foo/bar/bat')
806            self.assertEqual(posixpath.relpath(b"/x", b"/foo/bar/bat"), b'../../../x')
807            self.assertEqual(posixpath.relpath(b"/", b"/"), b'.')
808            self.assertEqual(posixpath.relpath(b"/a", b"/a"), b'.')
809            self.assertEqual(posixpath.relpath(b"/a/b", b"/a/b"), b'.')
810
811            self.assertRaises(TypeError, posixpath.relpath, b"bytes", "str")
812            self.assertRaises(TypeError, posixpath.relpath, "str", b"bytes")
813        finally:
814            os.getcwdb = real_getcwdb
815
816    def test_commonpath(self):
817        def check(paths, expected):
818            self.assertEqual(posixpath.commonpath(paths), expected)
819            self.assertEqual(posixpath.commonpath([os.fsencode(p) for p in paths]),
820                             os.fsencode(expected))
821        def check_error(exc, paths):
822            self.assertRaises(exc, posixpath.commonpath, paths)
823            self.assertRaises(exc, posixpath.commonpath,
824                              [os.fsencode(p) for p in paths])
825
826        self.assertRaises(TypeError, posixpath.commonpath, None)
827        self.assertRaises(ValueError, posixpath.commonpath, [])
828        self.assertRaises(ValueError, posixpath.commonpath, iter([]))
829        check_error(ValueError, ['/usr', 'usr'])
830        check_error(ValueError, ['usr', '/usr'])
831
832        check(['/usr/local'], '/usr/local')
833        check(['/usr/local', '/usr/local'], '/usr/local')
834        check(['/usr/local/', '/usr/local'], '/usr/local')
835        check(['/usr/local/', '/usr/local/'], '/usr/local')
836        check(['/usr//local', '//usr/local'], '/usr/local')
837        check(['/usr/./local', '/./usr/local'], '/usr/local')
838        check(['/', '/dev'], '/')
839        check(['/usr', '/dev'], '/')
840        check(['/usr/lib/', '/usr/lib/python3'], '/usr/lib')
841        check(['/usr/lib/', '/usr/lib64/'], '/usr')
842
843        check(['/usr/lib', '/usr/lib64'], '/usr')
844        check(['/usr/lib/', '/usr/lib64'], '/usr')
845
846        check(['spam'], 'spam')
847        check(['spam', 'spam'], 'spam')
848        check(['spam', 'alot'], '')
849        check(['and/jam', 'and/spam'], 'and')
850        check(['and//jam', 'and/spam//'], 'and')
851        check(['and/./jam', './and/spam'], 'and')
852        check(['and/jam', 'and/spam', 'alot'], '')
853        check(['and/jam', 'and/spam', 'and'], 'and')
854
855        check([''], '')
856        check(['', 'spam/alot'], '')
857        check_error(ValueError, ['', '/spam/alot'])
858
859        self.assertRaises(TypeError, posixpath.commonpath,
860                          [b'/usr/lib/', '/usr/lib/python3'])
861        self.assertRaises(TypeError, posixpath.commonpath,
862                          [b'/usr/lib/', 'usr/lib/python3'])
863        self.assertRaises(TypeError, posixpath.commonpath,
864                          [b'usr/lib/', '/usr/lib/python3'])
865        self.assertRaises(TypeError, posixpath.commonpath,
866                          ['/usr/lib/', b'/usr/lib/python3'])
867        self.assertRaises(TypeError, posixpath.commonpath,
868                          ['/usr/lib/', b'usr/lib/python3'])
869        self.assertRaises(TypeError, posixpath.commonpath,
870                          ['usr/lib/', b'/usr/lib/python3'])
871
872
873class PosixCommonTest(test_genericpath.CommonTest, unittest.TestCase):
874    pathmodule = posixpath
875    attributes = ['relpath', 'samefile', 'sameopenfile', 'samestat']
876
877
878class PathLikeTests(unittest.TestCase):
879
880    path = posixpath
881
882    def setUp(self):
883        self.file_name = os_helper.TESTFN
884        self.file_path = FakePath(os_helper.TESTFN)
885        self.addCleanup(os_helper.unlink, self.file_name)
886        with open(self.file_name, 'xb', 0) as file:
887            file.write(b"test_posixpath.PathLikeTests")
888
889    def assertPathEqual(self, func):
890        self.assertEqual(func(self.file_path), func(self.file_name))
891
892    def test_path_normcase(self):
893        self.assertPathEqual(self.path.normcase)
894
895    def test_path_isabs(self):
896        self.assertPathEqual(self.path.isabs)
897
898    def test_path_join(self):
899        self.assertEqual(self.path.join('a', FakePath('b'), 'c'),
900                         self.path.join('a', 'b', 'c'))
901
902    def test_path_split(self):
903        self.assertPathEqual(self.path.split)
904
905    def test_path_splitext(self):
906        self.assertPathEqual(self.path.splitext)
907
908    def test_path_splitdrive(self):
909        self.assertPathEqual(self.path.splitdrive)
910
911    def test_path_splitroot(self):
912        self.assertPathEqual(self.path.splitroot)
913
914    def test_path_basename(self):
915        self.assertPathEqual(self.path.basename)
916
917    def test_path_dirname(self):
918        self.assertPathEqual(self.path.dirname)
919
920    def test_path_islink(self):
921        self.assertPathEqual(self.path.islink)
922
923    def test_path_lexists(self):
924        self.assertPathEqual(self.path.lexists)
925
926    def test_path_ismount(self):
927        self.assertPathEqual(self.path.ismount)
928
929    def test_path_expanduser(self):
930        self.assertPathEqual(self.path.expanduser)
931
932    def test_path_expandvars(self):
933        self.assertPathEqual(self.path.expandvars)
934
935    def test_path_normpath(self):
936        self.assertPathEqual(self.path.normpath)
937
938    def test_path_abspath(self):
939        self.assertPathEqual(self.path.abspath)
940
941    def test_path_realpath(self):
942        self.assertPathEqual(self.path.realpath)
943
944    def test_path_relpath(self):
945        self.assertPathEqual(self.path.relpath)
946
947    def test_path_commonpath(self):
948        common_path = self.path.commonpath([self.file_path, self.file_name])
949        self.assertEqual(common_path, self.file_name)
950
951
952if __name__=="__main__":
953    unittest.main()
954