tester("ntpath.normpath('\\\\.\\NUL')", r'\\.\NUL')
tester("ntpath.normpath('\\\\?\\D:/XY\\Z')", r'\\?\D:/XY\Z')
+ tester("ntpath.normpath('handbook/../../Tests/image.png')", r'..\Tests\image.png')
+ tester("ntpath.normpath('handbook/../../../Tests/image.png')", r'..\..\Tests\image.png')
+ tester("ntpath.normpath('handbook///../a/.././../b/c')", r'..\b\c')
+ tester("ntpath.normpath('handbook/a/../..///../../b/c')", r'..\..\b\c')
+
+ tester("ntpath.normpath('//server/share/..')" , '\\\\server\\share\\')
+ tester("ntpath.normpath('//server/share/../')" , '\\\\server\\share\\')
+ tester("ntpath.normpath('//server/share/../..')", '\\\\server\\share\\')
+ tester("ntpath.normpath('//server/share/../../')", '\\\\server\\share\\')
def test_realpath_curdir(self):
expected = ntpath.normpath(os.getcwd())
("/..", "/"),
("/../", "/"),
("/..//", "/"),
+ ("//.", "//"),
("//..", "//"),
+ ("//...", "//..."),
+ ("//../foo", "//foo"),
+ ("//../../foo", "//foo"),
("/../foo", "/foo"),
("/../../foo", "/foo"),
("/../foo/../", "/"),
("/../foo/../bar", "/bar"),
("/../../foo/../bar/./baz/boom/..", "/bar/baz"),
("/../../foo/../bar/./baz/boom/.", "/bar/baz/boom"),
+ ("foo/../bar/baz", "bar/baz"),
+ ("foo/../../bar/baz", "../bar/baz"),
+ ("foo/../../../bar/baz", "../../bar/baz"),
+ ("foo///../bar/.././../baz/boom", "../baz/boom"),
+ ("foo/bar/../..///../../baz/boom", "../../baz/boom"),
+ ("/foo/..", "/"),
+ ("/foo/../..", "/"),
+ ("//foo/..", "//"),
+ ("//foo/../..", "//"),
+ ("///foo/..", "/"),
+ ("///foo/../..", "/"),
+ ("////foo/..", "/"),
+ ("/////foo/..", "/"),
]
def test_normpath(self):
--- /dev/null
+Fix the regression of os.path.normpath("A/../../B") not returning expected "../B" but "B".
\ No newline at end of file
if (!path[0] || size == 0) {
return path;
}
- wchar_t lastC = L'\0';
- wchar_t *p1 = path;
wchar_t *pEnd = size >= 0 ? &path[size] : NULL;
- wchar_t *p2 = path;
- wchar_t *minP2 = path;
+ wchar_t *p1 = path; // sequentially scanned address in the path
+ wchar_t *p2 = path; // destination of a scanned character to be ljusted
+ wchar_t *minP2 = path; // the beginning of the destination range
+ wchar_t lastC = L'\0'; // the last ljusted character, p2[-1] in most cases
#define IS_END(x) (pEnd ? (x) == pEnd : !*(x))
#ifdef ALTSEP
*p2++ = lastC = *p1;
}
}
- minP2 = p2;
+ if (sepCount) {
+ minP2 = p2; // Invalid path
+ } else {
+ minP2 = p2 - 1; // Absolute path has SEP at minP2
+ }
}
#else
// Skip past two leading SEPs
else if (IS_SEP(&p1[0]) && IS_SEP(&p1[1]) && !IS_SEP(&p1[2])) {
*p2++ = *p1++;
*p2++ = *p1++;
- minP2 = p2;
+ minP2 = p2 - 1; // Absolute path has SEP at minP2
lastC = SEP;
}
#endif /* MS_WINDOWS */
wchar_t *p3 = p2;
while (p3 != minP2 && *--p3 == SEP) { }
while (p3 != minP2 && *(p3 - 1) != SEP) { --p3; }
- if (p3[0] == L'.' && p3[1] == L'.' && IS_SEP(&p3[2])) {
- // Previous segment is also ../, so append instead
+ if (p2 == minP2
+ || (p3[0] == L'.' && p3[1] == L'.' && IS_SEP(&p3[2])))
+ {
+ // Previous segment is also ../, so append instead.
+ // Relative path does not absorb ../ at minP2 as well.
*p2++ = L'.';
*p2++ = L'.';
lastC = L'.';
}
} else {
*p2++ = lastC = c;
- }
+ }
}
*p2 = L'\0';
if (p2 != minP2) {