• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import unittest
2from test import test_support, test_genericpath
3from test import test_support as support
4
5import posixpath
6import os
7import sys
8from posixpath import realpath, abspath, dirname, basename
9
10try:
11    import posix
12except ImportError:
13    posix = None
14
15# An absolute path to a temporary filename for testing. We can't rely on TESTFN
16# being an absolute path, so we need this.
17
18ABSTFN = abspath(test_support.TESTFN)
19
20def skip_if_ABSTFN_contains_backslash(test):
21    """
22    On Windows, posixpath.abspath still returns paths with backslashes
23    instead of posix forward slashes. If this is the case, several tests
24    fail, so skip them.
25    """
26    found_backslash = '\\' in ABSTFN
27    msg = "ABSTFN is not a posix path - tests fail"
28    return [test, unittest.skip(msg)(test)][found_backslash]
29
30def safe_rmdir(dirname):
31    try:
32        os.rmdir(dirname)
33    except OSError:
34        pass
35
36class PosixPathTest(unittest.TestCase):
37
38    def setUp(self):
39        self.tearDown()
40
41    def tearDown(self):
42        for suffix in ["", "1", "2"]:
43            test_support.unlink(test_support.TESTFN + suffix)
44            safe_rmdir(test_support.TESTFN + suffix)
45
46    def test_join(self):
47        self.assertEqual(posixpath.join("/foo", "bar", "/bar", "baz"), "/bar/baz")
48        self.assertEqual(posixpath.join("/foo", "bar", "baz"), "/foo/bar/baz")
49        self.assertEqual(posixpath.join("/foo/", "bar/", "baz/"), "/foo/bar/baz/")
50
51    def test_split(self):
52        self.assertEqual(posixpath.split("/foo/bar"), ("/foo", "bar"))
53        self.assertEqual(posixpath.split("/"), ("/", ""))
54        self.assertEqual(posixpath.split("foo"), ("", "foo"))
55        self.assertEqual(posixpath.split("////foo"), ("////", "foo"))
56        self.assertEqual(posixpath.split("//foo//bar"), ("//foo", "bar"))
57
58    def splitextTest(self, path, filename, ext):
59        self.assertEqual(posixpath.splitext(path), (filename, ext))
60        self.assertEqual(posixpath.splitext("/" + path), ("/" + filename, ext))
61        self.assertEqual(posixpath.splitext("abc/" + path), ("abc/" + filename, ext))
62        self.assertEqual(posixpath.splitext("abc.def/" + path), ("abc.def/" + filename, ext))
63        self.assertEqual(posixpath.splitext("/abc.def/" + path), ("/abc.def/" + filename, ext))
64        self.assertEqual(posixpath.splitext(path + "/"), (filename + ext + "/", ""))
65
66    def test_splitext(self):
67        self.splitextTest("foo.bar", "foo", ".bar")
68        self.splitextTest("foo.boo.bar", "foo.boo", ".bar")
69        self.splitextTest("foo.boo.biff.bar", "foo.boo.biff", ".bar")
70        self.splitextTest(".csh.rc", ".csh", ".rc")
71        self.splitextTest("nodots", "nodots", "")
72        self.splitextTest(".cshrc", ".cshrc", "")
73        self.splitextTest("...manydots", "...manydots", "")
74        self.splitextTest("...manydots.ext", "...manydots", ".ext")
75        self.splitextTest(".", ".", "")
76        self.splitextTest("..", "..", "")
77        self.splitextTest("........", "........", "")
78        self.splitextTest("", "", "")
79
80    def test_isabs(self):
81        self.assertIs(posixpath.isabs(""), False)
82        self.assertIs(posixpath.isabs("/"), True)
83        self.assertIs(posixpath.isabs("/foo"), True)
84        self.assertIs(posixpath.isabs("/foo/bar"), True)
85        self.assertIs(posixpath.isabs("foo/bar"), False)
86
87    def test_basename(self):
88        self.assertEqual(posixpath.basename("/foo/bar"), "bar")
89        self.assertEqual(posixpath.basename("/"), "")
90        self.assertEqual(posixpath.basename("foo"), "foo")
91        self.assertEqual(posixpath.basename("////foo"), "foo")
92        self.assertEqual(posixpath.basename("//foo//bar"), "bar")
93
94    def test_dirname(self):
95        self.assertEqual(posixpath.dirname("/foo/bar"), "/foo")
96        self.assertEqual(posixpath.dirname("/"), "/")
97        self.assertEqual(posixpath.dirname("foo"), "")
98        self.assertEqual(posixpath.dirname("////foo"), "////")
99        self.assertEqual(posixpath.dirname("//foo//bar"), "//foo")
100
101    def test_islink(self):
102        self.assertIs(posixpath.islink(test_support.TESTFN + "1"), False)
103        f = open(test_support.TESTFN + "1", "wb")
104        try:
105            f.write("foo")
106            f.close()
107            self.assertIs(posixpath.islink(test_support.TESTFN + "1"), False)
108            if hasattr(os, 'symlink'):
109                os.symlink(test_support.TESTFN + "1", test_support.TESTFN + "2")
110                self.assertIs(posixpath.islink(test_support.TESTFN + "2"), True)
111                os.remove(test_support.TESTFN + "1")
112                self.assertIs(posixpath.islink(test_support.TESTFN + "2"), True)
113                self.assertIs(posixpath.exists(test_support.TESTFN + "2"), False)
114                self.assertIs(posixpath.lexists(test_support.TESTFN + "2"), True)
115        finally:
116            if not f.close():
117                f.close()
118
119    def test_samefile(self):
120        f = open(test_support.TESTFN + "1", "wb")
121        try:
122            f.write("foo")
123            f.close()
124            self.assertIs(
125                posixpath.samefile(
126                    test_support.TESTFN + "1",
127                    test_support.TESTFN + "1"
128                ),
129                True
130            )
131
132            # If we don't have links, assume that os.stat doesn't return
133            # reasonable inode information and thus, that samefile() doesn't
134            # work.
135            if hasattr(os, "symlink"):
136                os.symlink(
137                    test_support.TESTFN + "1",
138                    test_support.TESTFN + "2"
139                )
140                self.assertIs(
141                    posixpath.samefile(
142                        test_support.TESTFN + "1",
143                        test_support.TESTFN + "2"
144                    ),
145                    True
146                )
147                os.remove(test_support.TESTFN + "2")
148                f = open(test_support.TESTFN + "2", "wb")
149                f.write("bar")
150                f.close()
151                self.assertIs(
152                    posixpath.samefile(
153                        test_support.TESTFN + "1",
154                        test_support.TESTFN + "2"
155                    ),
156                    False
157                )
158        finally:
159            if not f.close():
160                f.close()
161
162    def test_samestat(self):
163        f = open(test_support.TESTFN + "1", "wb")
164        try:
165            f.write("foo")
166            f.close()
167            self.assertIs(
168                posixpath.samestat(
169                    os.stat(test_support.TESTFN + "1"),
170                    os.stat(test_support.TESTFN + "1")
171                ),
172                True
173            )
174            # If we don't have links, assume that os.stat() doesn't return
175            # reasonable inode information and thus, that samestat() doesn't
176            # work.
177            if hasattr(os, "symlink"):
178                os.symlink(test_support.TESTFN + "1", test_support.TESTFN + "2")
179                self.assertIs(
180                    posixpath.samestat(
181                        os.stat(test_support.TESTFN + "1"),
182                        os.stat(test_support.TESTFN + "2")
183                    ),
184                    True
185                )
186                os.remove(test_support.TESTFN + "2")
187                f = open(test_support.TESTFN + "2", "wb")
188                f.write("bar")
189                f.close()
190                self.assertIs(
191                    posixpath.samestat(
192                        os.stat(test_support.TESTFN + "1"),
193                        os.stat(test_support.TESTFN + "2")
194                    ),
195                    False
196                )
197        finally:
198            if not f.close():
199                f.close()
200
201    def test_ismount(self):
202        self.assertIs(posixpath.ismount("/"), True)
203
204    def test_ismount_non_existent(self):
205        # Non-existent mountpoint.
206        self.assertIs(posixpath.ismount(ABSTFN), False)
207        try:
208            os.mkdir(ABSTFN)
209            self.assertIs(posixpath.ismount(ABSTFN), False)
210        finally:
211            safe_rmdir(ABSTFN)
212
213    @unittest.skipUnless(hasattr(os, 'symlink'),
214                         'Requires functional symlink implementation')
215    def test_ismount_symlinks(self):
216        # Symlinks are never mountpoints.
217        try:
218            os.symlink("/", ABSTFN)
219            self.assertIs(posixpath.ismount(ABSTFN), False)
220        finally:
221            os.unlink(ABSTFN)
222
223    @unittest.skipIf(posix is None, "Test requires posix module")
224    def test_ismount_different_device(self):
225        # Simulate the path being on a different device from its parent by
226        # mocking out st_dev.
227        save_lstat = os.lstat
228        def fake_lstat(path):
229            st_ino = 0
230            st_dev = 0
231            if path == ABSTFN:
232                st_dev = 1
233                st_ino = 1
234            return posix.stat_result((0, st_ino, st_dev, 0, 0, 0, 0, 0, 0, 0))
235        try:
236            os.lstat = fake_lstat
237            self.assertIs(posixpath.ismount(ABSTFN), True)
238        finally:
239            os.lstat = save_lstat
240
241    @unittest.skipIf(posix is None, "Test requires posix module")
242    def test_ismount_directory_not_readable(self):
243        # issue #2466: Simulate ismount run on a directory that is not
244        # readable, which used to return False.
245        save_lstat = os.lstat
246        def fake_lstat(path):
247            st_ino = 0
248            st_dev = 0
249            if path.startswith(ABSTFN) and path != ABSTFN:
250                # ismount tries to read something inside the ABSTFN directory;
251                # simulate this being forbidden (no read permission).
252                raise OSError("Fake [Errno 13] Permission denied")
253            if path == ABSTFN:
254                st_dev = 1
255                st_ino = 1
256            return posix.stat_result((0, st_ino, st_dev, 0, 0, 0, 0, 0, 0, 0))
257        try:
258            os.lstat = fake_lstat
259            self.assertIs(posixpath.ismount(ABSTFN), True)
260        finally:
261            os.lstat = save_lstat
262
263    def test_expanduser(self):
264        self.assertEqual(posixpath.expanduser("foo"), "foo")
265        with test_support.EnvironmentVarGuard() as env:
266            for home in '/', '', '//', '///':
267                env['HOME'] = home
268                self.assertEqual(posixpath.expanduser("~"), "/")
269                self.assertEqual(posixpath.expanduser("~/"), "/")
270                self.assertEqual(posixpath.expanduser("~/foo"), "/foo")
271        try:
272            import pwd
273        except ImportError:
274            pass
275        else:
276            self.assertIsInstance(posixpath.expanduser("~/"), basestring)
277            # if home directory == root directory, this test makes no sense
278            if posixpath.expanduser("~") != '/':
279                self.assertEqual(
280                    posixpath.expanduser("~") + "/",
281                    posixpath.expanduser("~/")
282                )
283            self.assertIsInstance(posixpath.expanduser("~root/"), basestring)
284            self.assertIsInstance(posixpath.expanduser("~foo/"), basestring)
285
286            with test_support.EnvironmentVarGuard() as env:
287                # expanduser should fall back to using the password database
288                del env['HOME']
289                home = pwd.getpwuid(os.getuid()).pw_dir
290                # $HOME can end with a trailing /, so strip it (see #17809)
291                home = home.rstrip("/") or '/'
292                self.assertEqual(posixpath.expanduser("~"), home)
293
294    def test_normpath(self):
295        self.assertEqual(posixpath.normpath(""), ".")
296        self.assertEqual(posixpath.normpath("/"), "/")
297        self.assertEqual(posixpath.normpath("//"), "//")
298        self.assertEqual(posixpath.normpath("///"), "/")
299        self.assertEqual(posixpath.normpath("///foo/.//bar//"), "/foo/bar")
300        self.assertEqual(posixpath.normpath("///foo/.//bar//.//..//.//baz"), "/foo/baz")
301        self.assertEqual(posixpath.normpath("///..//./foo/.//bar"), "/foo/bar")
302
303    @skip_if_ABSTFN_contains_backslash
304    def test_realpath_curdir(self):
305        self.assertEqual(realpath('.'), os.getcwd())
306        self.assertEqual(realpath('./.'), os.getcwd())
307        self.assertEqual(realpath('/'.join(['.'] * 100)), os.getcwd())
308
309    @skip_if_ABSTFN_contains_backslash
310    def test_realpath_pardir(self):
311        self.assertEqual(realpath('..'), dirname(os.getcwd()))
312        self.assertEqual(realpath('../..'), dirname(dirname(os.getcwd())))
313        self.assertEqual(realpath('/'.join(['..'] * 100)), '/')
314
315    if hasattr(os, "symlink"):
316        def test_realpath_basic(self):
317            # Basic operation.
318            try:
319                os.symlink(ABSTFN+"1", ABSTFN)
320                self.assertEqual(realpath(ABSTFN), ABSTFN+"1")
321            finally:
322                test_support.unlink(ABSTFN)
323
324        def test_realpath_symlink_loops(self):
325            # Bug #930024, return the path unchanged if we get into an infinite
326            # symlink loop.
327            try:
328                os.symlink(ABSTFN, ABSTFN)
329                self.assertEqual(realpath(ABSTFN), ABSTFN)
330
331                os.symlink(ABSTFN+"1", ABSTFN+"2")
332                os.symlink(ABSTFN+"2", ABSTFN+"1")
333                self.assertEqual(realpath(ABSTFN+"1"), ABSTFN+"1")
334                self.assertEqual(realpath(ABSTFN+"2"), ABSTFN+"2")
335
336                self.assertEqual(realpath(ABSTFN+"1/x"), ABSTFN+"1/x")
337                self.assertEqual(realpath(ABSTFN+"1/.."), dirname(ABSTFN))
338                self.assertEqual(realpath(ABSTFN+"1/../x"), dirname(ABSTFN) + "/x")
339                os.symlink(ABSTFN+"x", ABSTFN+"y")
340                self.assertEqual(realpath(ABSTFN+"1/../" + basename(ABSTFN) + "y"),
341                                ABSTFN + "y")
342                self.assertEqual(realpath(ABSTFN+"1/../" + basename(ABSTFN) + "1"),
343                                ABSTFN + "1")
344
345                os.symlink(basename(ABSTFN) + "a/b", ABSTFN+"a")
346                self.assertEqual(realpath(ABSTFN+"a"), ABSTFN+"a/b")
347
348                os.symlink("../" + basename(dirname(ABSTFN)) + "/" +
349                        basename(ABSTFN) + "c", ABSTFN+"c")
350                self.assertEqual(realpath(ABSTFN+"c"), ABSTFN+"c")
351
352                # Test using relative path as well.
353                with support.change_cwd(dirname(ABSTFN)):
354                    self.assertEqual(realpath(basename(ABSTFN)), ABSTFN)
355            finally:
356                test_support.unlink(ABSTFN)
357                test_support.unlink(ABSTFN+"1")
358                test_support.unlink(ABSTFN+"2")
359                test_support.unlink(ABSTFN+"y")
360                test_support.unlink(ABSTFN+"c")
361                test_support.unlink(ABSTFN+"a")
362
363        def test_realpath_repeated_indirect_symlinks(self):
364            # Issue #6975.
365            try:
366                os.mkdir(ABSTFN)
367                os.symlink('../' + basename(ABSTFN), ABSTFN + '/self')
368                os.symlink('self/self/self', ABSTFN + '/link')
369                self.assertEqual(realpath(ABSTFN + '/link'), ABSTFN)
370            finally:
371                test_support.unlink(ABSTFN + '/self')
372                test_support.unlink(ABSTFN + '/link')
373                safe_rmdir(ABSTFN)
374
375        def test_realpath_deep_recursion(self):
376            depth = 10
377            try:
378                os.mkdir(ABSTFN)
379                for i in range(depth):
380                    os.symlink('/'.join(['%d' % i] * 10), ABSTFN + '/%d' % (i + 1))
381                os.symlink('.', ABSTFN + '/0')
382                self.assertEqual(realpath(ABSTFN + '/%d' % depth), ABSTFN)
383
384                # Test using relative path as well.
385                with support.change_cwd(ABSTFN):
386                    self.assertEqual(realpath('%d' % depth), ABSTFN)
387            finally:
388                for i in range(depth + 1):
389                    test_support.unlink(ABSTFN + '/%d' % i)
390                safe_rmdir(ABSTFN)
391
392        def test_realpath_resolve_parents(self):
393            # We also need to resolve any symlinks in the parents of a relative
394            # path passed to realpath. E.g.: current working directory is
395            # /usr/doc with 'doc' being a symlink to /usr/share/doc. We call
396            # realpath("a"). This should return /usr/share/doc/a/.
397            try:
398                os.mkdir(ABSTFN)
399                os.mkdir(ABSTFN + "/y")
400                os.symlink(ABSTFN + "/y", ABSTFN + "/k")
401
402                with support.change_cwd(ABSTFN + "/k"):
403                    self.assertEqual(realpath("a"), ABSTFN + "/y/a")
404            finally:
405                test_support.unlink(ABSTFN + "/k")
406                safe_rmdir(ABSTFN + "/y")
407                safe_rmdir(ABSTFN)
408
409        def test_realpath_resolve_before_normalizing(self):
410            # Bug #990669: Symbolic links should be resolved before we
411            # normalize the path. E.g.: if we have directories 'a', 'k' and 'y'
412            # in the following hierarchy:
413            # a/k/y
414            #
415            # and a symbolic link 'link-y' pointing to 'y' in directory 'a',
416            # then realpath("link-y/..") should return 'k', not 'a'.
417            try:
418                os.mkdir(ABSTFN)
419                os.mkdir(ABSTFN + "/k")
420                os.mkdir(ABSTFN + "/k/y")
421                os.symlink(ABSTFN + "/k/y", ABSTFN + "/link-y")
422
423                # Absolute path.
424                self.assertEqual(realpath(ABSTFN + "/link-y/.."), ABSTFN + "/k")
425                # Relative path.
426                with support.change_cwd(dirname(ABSTFN)):
427                    self.assertEqual(realpath(basename(ABSTFN) + "/link-y/.."),
428                                     ABSTFN + "/k")
429            finally:
430                test_support.unlink(ABSTFN + "/link-y")
431                safe_rmdir(ABSTFN + "/k/y")
432                safe_rmdir(ABSTFN + "/k")
433                safe_rmdir(ABSTFN)
434
435        def test_realpath_resolve_first(self):
436            # Bug #1213894: The first component of the path, if not absolute,
437            # must be resolved too.
438
439            try:
440                os.mkdir(ABSTFN)
441                os.mkdir(ABSTFN + "/k")
442                os.symlink(ABSTFN, ABSTFN + "link")
443                with support.change_cwd(dirname(ABSTFN)):
444                    base = basename(ABSTFN)
445                    self.assertEqual(realpath(base + "link"), ABSTFN)
446                    self.assertEqual(realpath(base + "link/k"), ABSTFN + "/k")
447            finally:
448                test_support.unlink(ABSTFN + "link")
449                safe_rmdir(ABSTFN + "/k")
450                safe_rmdir(ABSTFN)
451
452    def test_relpath(self):
453        (real_getcwd, os.getcwd) = (os.getcwd, lambda: r"/home/user/bar")
454        try:
455            curdir = os.path.split(os.getcwd())[-1]
456            self.assertRaises(ValueError, posixpath.relpath, "")
457            self.assertEqual(posixpath.relpath("a"), "a")
458            self.assertEqual(posixpath.relpath(posixpath.abspath("a")), "a")
459            self.assertEqual(posixpath.relpath("a/b"), "a/b")
460            self.assertEqual(posixpath.relpath("../a/b"), "../a/b")
461            self.assertEqual(posixpath.relpath("a", "../b"), "../"+curdir+"/a")
462            self.assertEqual(posixpath.relpath("a/b", "../c"), "../"+curdir+"/a/b")
463            self.assertEqual(posixpath.relpath("a", "b/c"), "../../a")
464            self.assertEqual(posixpath.relpath("a", "a"), ".")
465            self.assertEqual(posixpath.relpath("/foo/bar/bat", "/x/y/z"), '../../../foo/bar/bat')
466            self.assertEqual(posixpath.relpath("/foo/bar/bat", "/foo/bar"), 'bat')
467            self.assertEqual(posixpath.relpath("/foo/bar/bat", "/"), 'foo/bar/bat')
468            self.assertEqual(posixpath.relpath("/", "/foo/bar/bat"), '../../..')
469            self.assertEqual(posixpath.relpath("/foo/bar/bat", "/x"), '../foo/bar/bat')
470            self.assertEqual(posixpath.relpath("/x", "/foo/bar/bat"), '../../../x')
471            self.assertEqual(posixpath.relpath("/", "/"), '.')
472            self.assertEqual(posixpath.relpath("/a", "/a"), '.')
473            self.assertEqual(posixpath.relpath("/a/b", "/a/b"), '.')
474        finally:
475            os.getcwd = real_getcwd
476
477    @test_support.requires_unicode
478    def test_expandvars_nonascii_word(self):
479        encoding = sys.getfilesystemencoding()
480        # Non-ASCII word characters
481        letters = test_support.u(r'\xe6\u0130\u0141\u03c6\u041a\u05d0\u062a\u0e01')
482        uwnonascii = letters.encode(encoding, 'ignore').decode(encoding)[:3]
483        swnonascii = uwnonascii.encode(encoding)
484        if not swnonascii:
485            self.skipTest('Needs non-ASCII word characters')
486        with test_support.EnvironmentVarGuard() as env:
487            env.clear()
488            env[swnonascii] = 'baz' + swnonascii
489            self.assertEqual(posixpath.expandvars(u'$%s bar' % uwnonascii),
490                             u'baz%s bar' % uwnonascii)
491
492
493class PosixCommonTest(test_genericpath.CommonTest):
494    pathmodule = posixpath
495    attributes = ['relpath', 'samefile', 'sameopenfile', 'samestat']
496
497
498def test_main():
499    test_support.run_unittest(PosixPathTest, PosixCommonTest)
500
501
502if __name__=="__main__":
503    test_main()
504