Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions Lib/test/test_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -736,7 +736,6 @@ def test_file_attributes(self):
result.st_file_attributes & stat.FILE_ATTRIBUTE_DIRECTORY,
stat.FILE_ATTRIBUTE_DIRECTORY)

@unittest.expectedFailureIfWindows('TODO: RUSTPYTHON; os.stat (PermissionError: [Errno 5] Access is denied.)')
@unittest.skipUnless(sys.platform == "win32", "Win32 specific tests")
def test_access_denied(self):
# Default to FindFirstFile WIN32_FIND_DATA when access is
Expand All @@ -759,7 +758,6 @@ def test_access_denied(self):
self.assertNotEqual(result.st_size, 0)
self.assertTrue(os.path.isfile(fname))

@unittest.expectedFailureIfWindows('TODO: RUSTPYTHON; os.stat (PermissionError: [Errno 1] Incorrect function.)')
@unittest.skipUnless(sys.platform == "win32", "Win32 specific tests")
def test_stat_block_device(self):
# bpo-38030: os.stat fails for block devices
Expand Down Expand Up @@ -1796,7 +1794,6 @@ def test_mode(self):
self.assertEqual(os.stat(path).st_mode & 0o777, 0o555)
self.assertEqual(os.stat(parent).st_mode & 0o777, 0o775)

@unittest.expectedFailureIfWindows('TODO: RUSTPYTHON; os.umask not implemented yet for all platforms')
@unittest.skipIf(
support.is_emscripten or support.is_wasi,
"Emscripten's/WASI's umask is a stub."
Expand Down Expand Up @@ -2116,7 +2113,6 @@ def test_urandom_fd_closed(self):
"""
rc, out, err = assert_python_ok('-Sc', code)

@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; (ModuleNotFoundError: No module named 'os'")
def test_urandom_fd_reopened(self):
# Issue #21207: urandom() should detect its fd to /dev/urandom
# changed to something else, and reopen it.
Expand Down Expand Up @@ -3221,7 +3217,6 @@ def test_getfinalpathname_handles(self):

self.assertEqual(0, handle_delta)

@unittest.expectedFailureIfWindows('TODO: RUSTPYTHON; os.stat (PermissionError: [Errno 5] Access is denied.)')
@support.requires_subprocess()
def test_stat_unlink_race(self):
# bpo-46785: the implementation of os.stat() falls back to reading
Expand Down Expand Up @@ -4927,7 +4922,6 @@ def setUp(self):
self.addCleanup(os_helper.rmtree, self.path)
os.mkdir(self.path)

@unittest.expectedFailure # TODO: RUSTPYTHON; (AssertionError: TypeError not raised by DirEntry)
def test_uninstantiable(self):
self.assertRaises(TypeError, os.DirEntry)

Expand Down Expand Up @@ -4976,7 +4970,6 @@ def assert_stat_equal(self, stat1, stat2, skip_fields):
else:
self.assertEqual(stat1, stat2)

@unittest.expectedFailure # TODO: RUSTPYTHON; (AssertionError: TypeError not raised by ScandirIter)
def test_uninstantiable(self):
scandir_iter = os.scandir(self.path)
self.assertRaises(TypeError, type(scandir_iter))
Expand Down Expand Up @@ -5230,7 +5223,6 @@ def test_fd(self):
st = os.stat(entry.name, dir_fd=fd, follow_symlinks=False)
self.assertEqual(entry.stat(follow_symlinks=False), st)

@unittest.expectedFailureIfWindows('TODO: RUSTPYTHON; (AssertionError: FileNotFoundError not raised by scandir)')
@unittest.skipIf(support.is_wasi, "WASI maps '' to cwd")
def test_empty_path(self):
self.assertRaises(FileNotFoundError, os.scandir, '')
Expand Down
1 change: 0 additions & 1 deletion Lib/test/test_shutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,6 @@ def onexc(*args):
self.assertEqual(errors[0][1], link)
self.assertIsInstance(errors[0][2], OSError)

@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
@os_helper.skip_unless_symlink
def test_rmtree_works_on_symlinks(self):
tmp = self.mkdtemp()
Expand Down
4 changes: 1 addition & 3 deletions crates/common/src/fileutils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ pub mod windows {
use crate::windows::ToWideString;
use libc::{S_IFCHR, S_IFDIR, S_IFMT};
use std::ffi::{CString, OsStr, OsString};
use std::os::windows::ffi::OsStrExt;
use std::os::windows::io::AsRawHandle;
use std::sync::OnceLock;
use windows_sys::Win32::Foundation::{
Expand Down Expand Up @@ -75,8 +74,7 @@ pub mod windows {
pub fn update_st_mode_from_path(&mut self, path: &OsStr, attr: u32) {
if attr & FILE_ATTRIBUTE_DIRECTORY == 0 {
let file_extension = path
.encode_wide()
.collect::<Vec<u16>>()
.to_wide()
.split(|&c| c == '.' as u16)
.next_back()
.and_then(|s| String::from_utf16(s).ok());
Expand Down
8 changes: 4 additions & 4 deletions crates/vm/src/stdlib/codecs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ mod _codecs {
#[cfg(windows)]
#[pyfunction]
fn mbcs_encode(args: MbcsEncodeArgs, vm: &VirtualMachine) -> PyResult<(Vec<u8>, usize)> {
use std::os::windows::ffi::OsStrExt;
use crate::common::windows::ToWideString;
use windows_sys::Win32::Globalization::{
CP_ACP, WC_NO_BEST_FIT_CHARS, WideCharToMultiByte,
};
Expand All @@ -259,7 +259,7 @@ mod _codecs {
}

// Convert UTF-8 string to UTF-16
let wide: Vec<u16> = std::ffi::OsStr::new(s).encode_wide().collect();
let wide: Vec<u16> = std::ffi::OsStr::new(s).to_wide();

// Get the required buffer size
let size = unsafe {
Expand Down Expand Up @@ -439,7 +439,7 @@ mod _codecs {
#[cfg(windows)]
#[pyfunction]
fn oem_encode(args: OemEncodeArgs, vm: &VirtualMachine) -> PyResult<(Vec<u8>, usize)> {
use std::os::windows::ffi::OsStrExt;
use crate::common::windows::ToWideString;
use windows_sys::Win32::Globalization::{
CP_OEMCP, WC_NO_BEST_FIT_CHARS, WideCharToMultiByte,
};
Expand All @@ -461,7 +461,7 @@ mod _codecs {
}

// Convert UTF-8 string to UTF-16
let wide: Vec<u16> = std::ffi::OsStr::new(s).encode_wide().collect();
let wide: Vec<u16> = std::ffi::OsStr::new(s).to_wide();

// Get the required buffer size
let size = unsafe {
Expand Down
10 changes: 3 additions & 7 deletions crates/vm/src/stdlib/nt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,15 @@ pub(crate) mod module {
use crate::{
PyResult, TryFromObject, VirtualMachine,
builtins::{PyBaseExceptionRef, PyDictRef, PyListRef, PyStrRef, PyTupleRef},
common::{crt_fd, os::last_os_error, suppress_iph},
common::{crt_fd, os::last_os_error, suppress_iph, windows::ToWideString},
convert::ToPyException,
function::{Either, OptionalArg},
ospath::OsPath,
stdlib::os::{_os, DirFd, FollowSymlinks, SupportFunc, TargetIsDirectory, errno_err},
};
use libc::intptr_t;
use std::os::windows::io::AsRawHandle;
use std::{
env, fs, io,
mem::MaybeUninit,
os::windows::ffi::{OsStrExt, OsStringExt},
};
use std::{env, fs, io, mem::MaybeUninit, os::windows::ffi::OsStringExt};
use windows_sys::Win32::{
Foundation::{self, INVALID_HANDLE_VALUE},
Storage::FileSystem,
Expand Down Expand Up @@ -541,7 +537,7 @@ pub(crate) mod module {

#[pyfunction]
fn _path_splitroot(path: OsPath, vm: &VirtualMachine) -> PyResult<(String, String)> {
let orig: Vec<_> = path.path.encode_wide().collect();
let orig: Vec<_> = path.path.to_wide();
if orig.is_empty() {
return Ok(("".to_owned(), "".to_owned()));
}
Expand Down
37 changes: 30 additions & 7 deletions crates/vm/src/stdlib/os.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ impl ToPyObject for crt_fd::Borrowed<'_> {
#[pymodule(sub)]
pub(super) mod _os {
use super::{DirFd, FollowSymlinks, SupportFunc, errno_err};
#[cfg(windows)]
use crate::common::windows::ToWideString;
use crate::{
AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
builtins::{
Expand All @@ -168,7 +170,7 @@ pub(super) mod _os {
ospath::{IOErrorBuilder, OsPath, OsPathOrFd, OutputMode},
protocol::PyIterReturn,
recursion::ReprGuard,
types::{IterNext, Iterable, PyStructSequence, Representable, SelfIter},
types::{IterNext, Iterable, PyStructSequence, Representable, SelfIter, Unconstructible},
utils::ToCString,
vm::VirtualMachine,
};
Expand Down Expand Up @@ -292,10 +294,29 @@ pub(super) mod _os {
#[pyfunction(name = "unlink")]
fn remove(path: OsPath, dir_fd: DirFd<'_, 0>, vm: &VirtualMachine) -> PyResult<()> {
let [] = dir_fd.0;
let is_junction = cfg!(windows)
&& fs::metadata(&path).is_ok_and(|meta| meta.file_type().is_dir())
&& fs::symlink_metadata(&path).is_ok_and(|meta| meta.file_type().is_symlink());
let res = if is_junction {
#[cfg(windows)]
let is_dir_link = {
// On Windows, we need to check if it's a directory symlink/junction
// using GetFileAttributesW, which doesn't follow symlinks.
// This is similar to CPython's Py_DeleteFileW.
use windows_sys::Win32::Storage::FileSystem::{
FILE_ATTRIBUTE_DIRECTORY, FILE_ATTRIBUTE_REPARSE_POINT, GetFileAttributesW,
INVALID_FILE_ATTRIBUTES,
};
let wide_path: Vec<u16> = path.path.as_os_str().to_wide_with_nul();
let attrs = unsafe { GetFileAttributesW(wide_path.as_ptr()) };
if attrs != INVALID_FILE_ATTRIBUTES {
let is_dir = (attrs & FILE_ATTRIBUTE_DIRECTORY) != 0;
let is_reparse = (attrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
is_dir && is_reparse
} else {
false
}
};
#[cfg(not(windows))]
let is_dir_link = false;

let res = if is_dir_link {
fs::remove_dir(&path)
} else {
fs::remove_file(&path)
Expand Down Expand Up @@ -474,7 +495,7 @@ pub(super) mod _os {
ino: AtomicCell<Option<u64>>,
}

#[pyclass(with(Representable))]
#[pyclass(with(Representable, Unconstructible))]
impl DirEntry {
#[pygetset]
fn name(&self, vm: &VirtualMachine) -> PyResult {
Expand Down Expand Up @@ -652,6 +673,7 @@ pub(super) mod _os {
}
}
}
impl Unconstructible for DirEntry {}

#[pyattr]
#[pyclass(name = "ScandirIter")]
Expand All @@ -661,7 +683,7 @@ pub(super) mod _os {
mode: OutputMode,
}

#[pyclass(with(IterNext, Iterable))]
#[pyclass(with(IterNext, Iterable, Unconstructible))]
impl ScandirIterator {
#[pymethod]
fn close(&self) {
Expand All @@ -679,6 +701,7 @@ pub(super) mod _os {
zelf.close()
}
}
impl Unconstructible for ScandirIterator {}
impl SelfIter for ScandirIterator {}
impl IterNext for ScandirIterator {
fn next(zelf: &crate::Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
Expand Down
13 changes: 3 additions & 10 deletions crates/vm/src/stdlib/sys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ mod sys {
vm::{Settings, VirtualMachine},
};
use num_traits::ToPrimitive;
#[cfg(windows)]
use std::os::windows::ffi::OsStrExt;
use std::{
env::{self, VarError},
io::Read,
Expand Down Expand Up @@ -551,12 +549,10 @@ mod sys {

#[cfg(windows)]
fn get_kernel32_version() -> std::io::Result<(u32, u32, u32)> {
use crate::common::windows::ToWideString;
unsafe {
// Create a wide string for "kernel32.dll"
let module_name: Vec<u16> = std::ffi::OsStr::new("kernel32.dll")
.encode_wide()
.chain(Some(0))
.collect();
let module_name: Vec<u16> = std::ffi::OsStr::new("kernel32.dll").to_wide_with_nul();
let h_kernel32 = GetModuleHandleW(module_name.as_ptr());
if h_kernel32.is_null() {
return Err(std::io::Error::last_os_error());
Expand Down Expand Up @@ -593,10 +589,7 @@ mod sys {
}

// Prepare an empty sub-block string (L"") as required by VerQueryValueW
let sub_block: Vec<u16> = std::ffi::OsStr::new("")
.encode_wide()
.chain(Some(0))
.collect();
let sub_block: Vec<u16> = std::ffi::OsStr::new("").to_wide_with_nul();

let mut ffi_ptr: *mut VS_FIXEDFILEINFO = std::ptr::null_mut();
let mut ffi_len: u32 = 0;
Expand Down
7 changes: 3 additions & 4 deletions crates/vm/src/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::{
convert::{ToPyObject, ToPyResult},
stdlib::os::errno_err,
};
use rustpython_common::windows::ToWideString;
use std::ffi::OsStr;
use windows::Win32::Foundation::HANDLE;
use windows_sys::Win32::Foundation::{HANDLE as RAW_HANDLE, INVALID_HANDLE_VALUE};
Expand Down Expand Up @@ -235,13 +236,12 @@ fn attributes_from_dir(
windows_sys::Win32::Storage::FileSystem::BY_HANDLE_FILE_INFORMATION,
u32,
)> {
use std::os::windows::ffi::OsStrExt;
use windows_sys::Win32::Storage::FileSystem::{
BY_HANDLE_FILE_INFORMATION, FILE_ATTRIBUTE_REPARSE_POINT, FindClose, FindFirstFileW,
WIN32_FIND_DATAW,
};

let wide: Vec<u16> = path.encode_wide().chain(std::iter::once(0)).collect();
let wide: Vec<u16> = path.to_wide_with_nul();
let mut find_data: WIN32_FIND_DATAW = unsafe { std::mem::zeroed() };

let handle = unsafe { FindFirstFileW(wide.as_ptr(), &mut find_data) };
Expand Down Expand Up @@ -269,7 +269,6 @@ fn attributes_from_dir(

/// Ported from win32_xstat_slow_impl
fn win32_xstat_slow_impl(path: &OsStr, traverse: bool) -> std::io::Result<StatStruct> {
use std::os::windows::ffi::OsStrExt;
use windows_sys::Win32::{
Foundation::{
CloseHandle, ERROR_ACCESS_DENIED, ERROR_CANT_ACCESS_FILE, ERROR_INVALID_FUNCTION,
Expand All @@ -287,7 +286,7 @@ fn win32_xstat_slow_impl(path: &OsStr, traverse: bool) -> std::io::Result<StatSt
},
};

let wide: Vec<u16> = path.encode_wide().chain(std::iter::once(0)).collect();
let wide: Vec<u16> = path.to_wide_with_nul();

let access = FILE_READ_ATTRIBUTES;
let mut flags = FILE_FLAG_BACKUP_SEMANTICS;
Expand Down
Loading