[Python-checkins] bpo-46208: Fix normalization of relative paths in _Py_normpath()/os.path.normpath (GH-30362)
zooba
webhook-mailer at python.org
Thu Jan 6 14:13:29 EST 2022
https://github.com/python/cpython/commit/9c5fa9c97c5c5336e60e4ae7a2e6e3f67acedfc7
commit: 9c5fa9c97c5c5336e60e4ae7a2e6e3f67acedfc7
branch: main
author: neonene <53406459+neonene at users.noreply.github.com>
committer: zooba <steve.dower at microsoft.com>
date: 2022-01-06T19:13:10Z
summary:
bpo-46208: Fix normalization of relative paths in _Py_normpath()/os.path.normpath (GH-30362)
files:
A Misc/NEWS.d/next/Core and Builtins/2022-01-04-01-53-35.bpo-46208.i00Vz5.rst
M Lib/test/test_ntpath.py
M Lib/test/test_posixpath.py
M Python/fileutils.c
diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py
index a8d87e53db9e9..cc29881049224 100644
--- a/Lib/test/test_ntpath.py
+++ b/Lib/test/test_ntpath.py
@@ -235,6 +235,15 @@ def test_normpath(self):
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())
diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py
index e4d8384ef0b4b..5fc4205beb125 100644
--- a/Lib/test/test_posixpath.py
+++ b/Lib/test/test_posixpath.py
@@ -329,13 +329,30 @@ def test_expanduser_pwd(self):
("/..", "/"),
("/../", "/"),
("/..//", "/"),
+ ("//.", "//"),
("//..", "//"),
+ ("//...", "//..."),
+ ("//../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):
diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-01-04-01-53-35.bpo-46208.i00Vz5.rst b/Misc/NEWS.d/next/Core and Builtins/2022-01-04-01-53-35.bpo-46208.i00Vz5.rst
new file mode 100644
index 0000000000000..92025a02d5b25
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2022-01-04-01-53-35.bpo-46208.i00Vz5.rst
@@ -0,0 +1 @@
+Fix the regression of os.path.normpath("A/../../B") not returning expected "../B" but "B".
\ No newline at end of file
diff --git a/Python/fileutils.c b/Python/fileutils.c
index cae6b75b6ae9b..d570be5577ae9 100644
--- a/Python/fileutils.c
+++ b/Python/fileutils.c
@@ -2218,11 +2218,11 @@ _Py_normpath(wchar_t *path, Py_ssize_t size)
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
@@ -2264,14 +2264,18 @@ _Py_normpath(wchar_t *path, Py_ssize_t size)
*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 */
@@ -2292,8 +2296,11 @@ _Py_normpath(wchar_t *path, Py_ssize_t size)
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'.';
@@ -2314,7 +2321,7 @@ _Py_normpath(wchar_t *path, Py_ssize_t size)
}
} else {
*p2++ = lastC = c;
- }
+ }
}
*p2 = L'\0';
if (p2 != minP2) {
More information about the Python-checkins
mailing list