diff --git a/Lib/pathlib.py b/Lib/pathlib.py
index 8d08e8b..66b549d 100644
--- a/Lib/pathlib.py
+++ b/Lib/pathlib.py
@@ -46,14 +46,18 @@ class _Flavour(object):
"""A flavour implements a particular (platform-specific) set of path
semantics."""
- def __init__(self):
- self.join = self.sep.join
+ def join(self, parts):
+ if parts and parts[-1] == '.':
+ return ''.join([i+self.sep for i in parts[:-1]])
+ else:
+ return self.sep.join(parts)
def parse_parts(self, parts):
parsed = []
sep = self.sep
altsep = self.altsep
drv = root = ''
+ dir_path = parts and parts[-1] and parts[-1][-1] in (sep, altsep)
it = reversed(parts)
for part in it:
if not part:
@@ -78,11 +82,21 @@ class _Flavour(object):
if drv:
break
break
+ if not parsed:
+ dir_path = False
if drv or root:
parsed.append(drv + root)
parsed.reverse()
+ if dir_path:
+ parsed.append('.')
return drv, root, parsed
+ def join_parts_only(self, parts, parts2):
+ if parts and parts[-1] == '.':
+ return parts[:-1] + parts2
+ else:
+ return parts + parts2
+
def join_parsed_parts(self, drv, root, parts, drv2, root2, parts2):
"""
Join the two paths represented by the respective
@@ -94,10 +108,10 @@ class _Flavour(object):
elif drv2:
if drv2 == drv or self.casefold(drv2) == self.casefold(drv):
# Same drive => second path is relative to the first
- return drv, root, parts + parts2[1:]
+ return drv, root, self.join_parts_only(parts, parts2[1:])
else:
# Second path is non-anchored (common case)
- return drv, root, parts + parts2
+ return drv, root, self.join_parts_only(parts, parts2)
return drv2, root2, parts2
@@ -795,6 +809,8 @@ class PurePath(object):
to_abs_parts = [to_drv, to_root] + to_parts[1:]
else:
to_abs_parts = to_parts
+ if to_abs_parts and to_abs_parts[-1] == '.':
+ del to_abs_parts[-1]
n = len(to_abs_parts)
cf = self._flavour.casefold_parts
if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts):
diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py
index 2268f94..4a651b6 100644
--- a/Lib/test/test_pathlib.py
+++ b/Lib/test/test_pathlib.py
@@ -39,11 +39,11 @@ class _BaseFlavourTest(object):
# Unanchored parts
check([], ('', '', []))
check(['a'], ('', '', ['a']))
- check(['a/'], ('', '', ['a']))
+ check(['a/'], ('', '', ['a', '.']))
check(['a', 'b'], ('', '', ['a', 'b']))
# Expansion
check(['a/b'], ('', '', ['a', 'b']))
- check(['a/b/'], ('', '', ['a', 'b']))
+ check(['a/b/'], ('', '', ['a', 'b', '.']))
check(['a', 'b/c', 'd'], ('', '', ['a', 'b', 'c', 'd']))
# Collapsing and stripping excess slashes
check(['a', 'b//c', 'd'], ('', '', ['a', 'b', 'c', 'd']))
@@ -121,7 +121,7 @@ class NTFlavourTest(_BaseFlavourTest, unittest.TestCase):
# UNC paths
check(['a', '\\\\b\\c', 'd'], ('\\\\b\\c', '\\', ['\\\\b\\c\\', 'd']))
# Collapsing and stripping excess slashes
- check(['a', 'Z:\\\\b\\\\c\\', 'd\\'], ('Z:', '\\', ['Z:\\', 'b', 'c', 'd']))
+ check(['a', 'Z:\\\\b\\\\c\\', 'd\\'], ('Z:', '\\', ['Z:\\', 'b', 'c', 'd', '.']))
# UNC paths
check(['a', '\\\\b\\c\\\\', 'd'], ('\\\\b\\c', '\\', ['\\\\b\\c\\', 'd']))
# Extended paths
@@ -166,17 +166,23 @@ class _BasePurePathTest(object):
# supposed to produce equal paths
equivalences = {
'a/b': [
- ('a', 'b'), ('a/', 'b'), ('a', 'b/'), ('a/', 'b/'),
- ('a/b/',), ('a//b',), ('a//b//',),
+ ('a', 'b'), ('a/', 'b'), ('a//b',),
# empty components get removed
('', 'a', 'b'), ('a', '', 'b'), ('a', 'b', ''),
],
+ 'a/b/': [
+ ('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'),
],
+ '/b/c/d/': [
+ ('a', '/b/c', 'd/'), ('a', '///b//c/', 'd/'),
+ ],
}
def setUp(self):
@@ -667,7 +673,8 @@ class PureWindowsPathTest(_BasePurePathTest, unittest.TestCase):
equivalences = _BasePurePathTest.equivalences.copy()
equivalences.update({
- 'c:a': [ ('c:', 'a'), ('c:', 'a/'), ('/', 'c:', 'a') ],
+ 'c:a': [ ('c:', 'a'), ('/', 'c:', 'a') ],
+ 'c:a/': [ ('c:', 'a/') ],
'c:/a': [
('c:/', 'a'), ('c:', '/', 'a'), ('c:', '/a'),
('/z', 'c:/', 'a'), ('//x/y', 'c:/', 'a'),
@@ -795,20 +802,22 @@ class PureWindowsPathTest(_BasePurePathTest, unittest.TestCase):
P = self.cls
p = P('z:a/b/')
par = p.parents
- self.assertEqual(len(par), 2)
- self.assertEqual(par[0], P('z:a'))
- self.assertEqual(par[1], P('z:'))
- self.assertEqual(list(par), [P('z:a'), P('z:')])
+ self.assertEqual(len(par), 3)
+ self.assertEqual(par[0], P('z:a/b'))
+ self.assertEqual(par[1], P('z:a'))
+ self.assertEqual(par[2], P('z:'))
+ self.assertEqual(list(par), [P('z:a/b'), P('z:a'), P('z:')])
with self.assertRaises(IndexError):
- par[2]
+ par[3]
p = P('z:/a/b/')
par = p.parents
- self.assertEqual(len(par), 2)
- self.assertEqual(par[0], P('z:/a'))
- self.assertEqual(par[1], P('z:/'))
- self.assertEqual(list(par), [P('z:/a'), P('z:/')])
+ self.assertEqual(len(par), 3)
+ self.assertEqual(par[0], P('z:/a/b'))
+ self.assertEqual(par[1], P('z:/a'))
+ self.assertEqual(par[2], P('z:/'))
+ self.assertEqual(list(par), [P('z:/a/b'),P('z:/a'), P('z:/')])
with self.assertRaises(IndexError):
- par[2]
+ par[3]
p = P('//a/b/c/d')
par = p.parents
self.assertEqual(len(par), 2)