Antoine Pitrou avatar Antoine Pitrou committed 67a2524

Issue #7: handle the special case of two leading slashes in POSIX paths

Comments (0)

Files changed (2)

 
     def splitroot(self, part, sep=sep):
         if part and part[0] == sep:
-            return '', sep, part.lstrip(sep)
+            stripped_part = part.lstrip(sep)
+            # According to POSIX path resolution:
+            # http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11
+            # "A pathname that begins with two successive slashes may be
+            # interpreted in an implementation-defined manner, although more
+            # than two leading slashes shall be treated as a single slash".
+            if len(part) - len(stripped_part) == 2:
+                return '', sep * 2, stripped_part
+            else:
+                return '', sep, stripped_part
         else:
             return '', '', part
 
 
     def test_parse_parts(self):
         check = self._check_parse_parts
+        # Collapsing of excess leading slashes, except for the double-slash
+        # special case.
+        check(['//a', 'b'],             ('', '//', ['//', 'a', 'b']))
+        check(['///a', 'b'],            ('', '/', ['/', 'a', 'b']))
+        check(['////a', 'b'],           ('', '/', ['/', 'a', 'b']))
         # Paths which look like NT paths aren't treated specially
         check(['c:a'],                  ('', '', ['c:a']))
         check(['c:\\a'],                ('', '', ['c:\\a']))
         self.assertEqual(f('/a/b'), ('', '/', 'a/b'))
         self.assertEqual(f('/a/b/'), ('', '/', 'a/b/'))
         # The root is collapsed when there are redundant slashes
-        self.assertEqual(f('//a'), ('', '/', 'a'))
+        # except when there are exactly two leading slashes, which
+        # is a special case in POSIX.
+        self.assertEqual(f('//a'), ('', '//', 'a'))
+        self.assertEqual(f('///a'), ('', '/', 'a'))
         self.assertEqual(f('///a/b'), ('', '/', 'a/b'))
         # Paths which look like NT paths aren't treated specially
         self.assertEqual(f('c:/a/b'), ('', '', 'c:/a/b'))
             ('', 'a', 'b'), ('a', '', 'b'), ('a', 'b', ''),
             ],
         '/b/c/d': [
-            ('a', '/b/c', 'd'), ('a', '//b//c', 'd/'),
+            ('a', '/b/c', 'd'), ('a', '///b//c', 'd/'),
             ('/a', '/b/c', 'd'),
             # empty components get removed
             ('/', 'b', '', 'c/d'), ('/', '', 'b/c/d'), ('', '/b/c/d'),
 
     def test_root(self):
         P = self.cls
-        # This is an UNC path under Windows
-        self.assertEqual(P('//a/b').root, '/')
+        self.assertEqual(P('/a/b').root, '/')
+        self.assertEqual(P('///a/b').root, '/')
+        # POSIX special case for two leading slashes
+        self.assertEqual(P('//a/b').root, '//')
 
     def test_eq(self):
         P = self.cls
         self.assertNotEqual(P('a/b'), P('A/b'))
+        self.assertEqual(P('/a'), P('///a'))
+        self.assertNotEqual(P('/a'), P('//a'))
 
     def test_match(self):
         P = self.cls
         self.assertTrue(P('/').is_absolute())
         self.assertTrue(P('/a').is_absolute())
         self.assertTrue(P('/a/b/').is_absolute())
+        self.assertTrue(P('//a').is_absolute())
+        self.assertTrue(P('//a/b').is_absolute())
 
     def test_normcase(self):
         P = self.cls
         self.assertIs(False, P('/foo/bar').is_reserved())
         self.assertIs(False, P('/dev/con/PRN/NUL').is_reserved())
 
+    def test_join(self):
+        P = self.cls
+        p = P('//a')
+        pp = p.join('b')
+        self.assertEqual(pp, P('//a/b'))
+        pp = P('/a').join('//c')
+        self.assertEqual(pp, P('//c'))
+        pp = P('//a').join('/c')
+        self.assertEqual(pp, P('/c'))
+
+    def test_getitem_common(self):
+        # Basically the same as join()
+        P = self.cls
+        p = P('//a')
+        pp = p['b']
+        self.assertEqual(pp, P('//a/b'))
+        pp = P('/a')['//c']
+        self.assertEqual(pp, P('//c'))
+        pp = P('//a')['/c']
+        self.assertEqual(pp, P('/c'))
+
 
 class PureNTPathTest(_BasePurePathTest):
     cls = pathlib.PureNTPath
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.