Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d25aef5

Browse files
committed
Fix #12084. os.stat on Windows wasn't working properly with relative symlinks.
Use of DeviceIoControl to obtain the symlink path via the reparse tag was removed. The code now uses GetFinalPathNameByHandle in the case of a symbolic link and works properly given the added test which creates a symbolic link and calls os.stat on it from multiple locations. Victor Stinner also noticed an issue with os.lstat following the os.stat code path when being passed bytes. The posix_lstat function was adjusted to properly hook up win32_lstat instead of the previous STAT macro (win32_stat).
1 parent 5b52f95 commit d25aef5

4 files changed

Lines changed: 192 additions & 97 deletions

File tree

Lib/test/support.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1490,7 +1490,7 @@ def can_symlink():
14901490
try:
14911491
os.symlink(TESTFN, TESTFN + "can_symlink")
14921492
can = True
1493-
except (OSError, NotImplementedError):
1493+
except (OSError, NotImplementedError, AttributeError):
14941494
can = False
14951495
_can_symlink = can
14961496
return can

Lib/test/test_os.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,6 +1243,51 @@ def check_stat(self, link, target):
12431243
self.assertEqual(os.stat(link), os.stat(target))
12441244
self.assertNotEqual(os.lstat(link), os.stat(link))
12451245

1246+
bytes_link = os.fsencode(link)
1247+
self.assertEqual(os.stat(bytes_link), os.stat(target))
1248+
self.assertNotEqual(os.lstat(bytes_link), os.stat(bytes_link))
1249+
1250+
def test_12084(self):
1251+
level1 = os.path.abspath(support.TESTFN)
1252+
level2 = os.path.join(level1, "level2")
1253+
level3 = os.path.join(level2, "level3")
1254+
try:
1255+
os.mkdir(level1)
1256+
os.mkdir(level2)
1257+
os.mkdir(level3)
1258+
1259+
file1 = os.path.abspath(os.path.join(level1, "file1"))
1260+
1261+
with open(file1, "w") as f:
1262+
f.write("file1")
1263+
1264+
orig_dir = os.getcwd()
1265+
try:
1266+
os.chdir(level2)
1267+
link = os.path.join(level2, "link")
1268+
os.symlink(os.path.relpath(file1), "link")
1269+
self.assertIn("link", os.listdir(os.getcwd()))
1270+
1271+
# Check os.stat calls from the same dir as the link
1272+
self.assertEqual(os.stat(file1), os.stat("link"))
1273+
1274+
# Check os.stat calls from a dir below the link
1275+
os.chdir(level1)
1276+
self.assertEqual(os.stat(file1),
1277+
os.stat(os.path.relpath(link)))
1278+
1279+
# Check os.stat calls from a dir above the link
1280+
os.chdir(level3)
1281+
self.assertEqual(os.stat(file1),
1282+
os.stat(os.path.relpath(link)))
1283+
finally:
1284+
os.chdir(orig_dir)
1285+
except OSError as err:
1286+
self.fail(err)
1287+
finally:
1288+
os.remove(file1)
1289+
shutil.rmtree(level1)
1290+
12461291

12471292
class FSEncodingTests(unittest.TestCase):
12481293
def test_nop(self):

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ What's New in Python 3.2.1 release candidate 2?
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #12084: os.stat on Windows now works properly with relative symbolic
14+
links when called from any directory.
15+
1316
- Issue #1195: my_fgets() now always clears errors before calling fgets(). Fix
1417
the following case: sys.stdin.read() stopped with CTRL+d (end of file),
1518
raw_input() interrupted by CTRL+c.

0 commit comments

Comments
 (0)