From 207a45620beff20a1b8df170599aa418ac55afbe Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" Date: Tue, 9 Dec 2025 00:43:36 +0900 Subject: [PATCH] scandir/lstat --- Lib/test/test_os.py | 3 --- crates/vm/src/stdlib/os.rs | 27 +++++++++++++++++++++++---- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 9e422fd162..d968aa87f0 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -4834,7 +4834,6 @@ class PathTConverterTests(unittest.TestCase): ('open', False, (os.O_RDONLY,), getattr(os, 'close', None)), ] - @unittest.expectedFailure # TODO: RUSTPYTHON; (AssertionError: TypeError not raised) def test_path_t_converter(self): str_filename = os_helper.TESTFN if os.name == 'nt': @@ -5109,7 +5108,6 @@ def test_fspath_protocol_bytes(self): self.assertEqual(fspath, os.path.join(os.fsencode(self.path),bytes_filename)) - @unittest.expectedFailureIfWindows('TODO: RUSTPYTHON; entry.is_dir() is False') def test_removed_dir(self): path = os.path.join(self.path, 'dir') @@ -5132,7 +5130,6 @@ def test_removed_dir(self): self.assertRaises(FileNotFoundError, entry.stat) self.assertRaises(FileNotFoundError, entry.stat, follow_symlinks=False) - @unittest.expectedFailureIfWindows('TODO: RUSTPYTHON; entry.is_file() is False') def test_removed_file(self): entry = self.create_file_entry() os.unlink(entry.path) diff --git a/crates/vm/src/stdlib/os.rs b/crates/vm/src/stdlib/os.rs index 3218599442..378cffd831 100644 --- a/crates/vm/src/stdlib/os.rs +++ b/crates/vm/src/stdlib/os.rs @@ -720,13 +720,32 @@ pub(super) mod _os { #[cfg(not(unix))] let ino = None; + let pathval = entry.path(); + + // On Windows, pre-cache lstat from directory entry metadata + // This allows stat() to return cached data even if file is removed + #[cfg(windows)] + let lstat = { + let cell = OnceCell::new(); + if let Ok(stat_struct) = + crate::windows::win32_xstat(pathval.as_os_str(), false) + { + let stat_obj = + StatResultData::from_stat(&stat_struct, vm).to_pyobject(vm); + let _ = cell.set(stat_obj); + } + cell + }; + #[cfg(not(windows))] + let lstat = OnceCell::new(); + Ok(PyIterReturn::Return( DirEntry { file_name: entry.file_name(), - pathval: entry.path(), + pathval, file_type: entry.file_type(), mode: zelf.mode, - lstat: OnceCell::new(), + lstat, stat: OnceCell::new(), ino: AtomicCell::new(ino), } @@ -974,11 +993,11 @@ pub(super) mod _os { #[pyfunction] fn lstat( - file: OsPathOrFd<'_>, + file: OsPath, dir_fd: DirFd<'_, { STAT_DIR_FD as usize }>, vm: &VirtualMachine, ) -> PyResult { - stat(file, dir_fd, FollowSymlinks(false), vm) + stat(file.into(), dir_fd, FollowSymlinks(false), vm) } fn curdir_inner(vm: &VirtualMachine) -> PyResult {