Skip to content

Commit 5c02ff2

Browse files
committed
impl more ntpath
1 parent 2063c1e commit 5c02ff2

File tree

3 files changed

+104
-6
lines changed

3 files changed

+104
-6
lines changed

Lib/test/test_ntpath.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1279,8 +1279,6 @@ def test_ismount(self):
12791279
self.assertTrue(ntpath.ismount(b"\\\\localhost\\c$"))
12801280
self.assertTrue(ntpath.ismount(b"\\\\localhost\\c$\\"))
12811281

1282-
# TODO: RUSTPYTHON
1283-
@unittest.skipIf(sys.platform == 'win32', "TODO: RUSTPYTHON; crash")
12841282
def test_ismount_invalid_paths(self):
12851283
ismount = ntpath.ismount
12861284
self.assertFalse(ismount("c:\\\udfff"))
@@ -1464,8 +1462,6 @@ def test_fast_paths_in_use(self):
14641462
self.assertTrue(os.path.lexists is nt._path_lexists)
14651463
self.assertFalse(inspect.isfunction(os.path.lexists))
14661464

1467-
# TODO: RUSTPYTHON
1468-
@unittest.expectedFailure
14691465
@unittest.skipIf(os.name != 'nt', "Dev Drives only exist on Win32")
14701466
def test_isdevdrive(self):
14711467
# Result may be True or False, but shouldn't raise

crates/vm/src/builtins/str.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -668,8 +668,19 @@ impl PyStr {
668668

669669
// casefold is much more aggressive than lower
670670
#[pymethod]
671-
fn casefold(&self) -> String {
672-
caseless::default_case_fold_str(self.as_str())
671+
fn casefold(&self) -> Self {
672+
match self.as_str_kind() {
673+
PyKindStr::Ascii(s) => caseless::default_case_fold_str(s.as_str()).into(),
674+
PyKindStr::Utf8(s) => caseless::default_case_fold_str(s).into(),
675+
PyKindStr::Wtf8(w) => w
676+
.chunks()
677+
.map(|c| match c {
678+
Wtf8Chunk::Utf8(s) => Wtf8Buf::from_string(caseless::default_case_fold_str(s)),
679+
Wtf8Chunk::Surrogate(c) => Wtf8Buf::from(c),
680+
})
681+
.collect::<Wtf8Buf>()
682+
.into(),
683+
}
673684
}
674685

675686
#[pymethod]

crates/vm/src/stdlib/nt.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,97 @@ pub(crate) mod module {
567567
.is_some_and(|p| _test_file_exists(&p, false))
568568
}
569569

570+
/// Check if a path is on a Windows Dev Drive.
571+
#[pyfunction]
572+
fn _path_isdevdrive(path: OsPath, vm: &VirtualMachine) -> PyResult<bool> {
573+
use windows_sys::Win32::Foundation::CloseHandle;
574+
use windows_sys::Win32::Storage::FileSystem::{
575+
CreateFileW, FILE_FLAG_BACKUP_SEMANTICS, FILE_READ_ATTRIBUTES, FILE_SHARE_READ,
576+
FILE_SHARE_WRITE, GetDriveTypeW, GetVolumePathNameW, OPEN_EXISTING,
577+
};
578+
use windows_sys::Win32::System::IO::DeviceIoControl;
579+
use windows_sys::Win32::System::Ioctl::FSCTL_QUERY_PERSISTENT_VOLUME_STATE;
580+
use windows_sys::Win32::System::WindowsProgramming::DRIVE_FIXED;
581+
582+
// PERSISTENT_VOLUME_STATE_DEV_VOLUME flag - not yet in windows-sys
583+
const PERSISTENT_VOLUME_STATE_DEV_VOLUME: u32 = 0x00002000;
584+
585+
// FILE_FS_PERSISTENT_VOLUME_INFORMATION structure
586+
#[repr(C)]
587+
struct FileFsPersistentVolumeInformation {
588+
volume_flags: u32,
589+
flag_mask: u32,
590+
version: u32,
591+
reserved: u32,
592+
}
593+
594+
let wide_path = path.to_wide_cstring(vm)?;
595+
let mut volume = [0u16; Foundation::MAX_PATH as usize];
596+
597+
// Get volume path
598+
let ret = unsafe {
599+
GetVolumePathNameW(wide_path.as_ptr(), volume.as_mut_ptr(), volume.len() as _)
600+
};
601+
if ret == 0 {
602+
return Err(vm.new_last_os_error());
603+
}
604+
605+
// Check if it's a fixed drive
606+
if unsafe { GetDriveTypeW(volume.as_ptr()) } != DRIVE_FIXED {
607+
return Ok(false);
608+
}
609+
610+
// Open the volume
611+
let handle = unsafe {
612+
CreateFileW(
613+
volume.as_ptr(),
614+
FILE_READ_ATTRIBUTES,
615+
FILE_SHARE_READ | FILE_SHARE_WRITE,
616+
std::ptr::null(),
617+
OPEN_EXISTING,
618+
FILE_FLAG_BACKUP_SEMANTICS,
619+
std::ptr::null_mut(),
620+
)
621+
};
622+
if handle == INVALID_HANDLE_VALUE {
623+
return Err(vm.new_last_os_error());
624+
}
625+
626+
// Query persistent volume state
627+
let mut volume_state = FileFsPersistentVolumeInformation {
628+
volume_flags: 0,
629+
flag_mask: PERSISTENT_VOLUME_STATE_DEV_VOLUME,
630+
version: 1,
631+
reserved: 0,
632+
};
633+
634+
let ret = unsafe {
635+
DeviceIoControl(
636+
handle,
637+
FSCTL_QUERY_PERSISTENT_VOLUME_STATE,
638+
&volume_state as *const _ as *const std::ffi::c_void,
639+
std::mem::size_of::<FileFsPersistentVolumeInformation>() as u32,
640+
&mut volume_state as *mut _ as *mut std::ffi::c_void,
641+
std::mem::size_of::<FileFsPersistentVolumeInformation>() as u32,
642+
std::ptr::null_mut(),
643+
std::ptr::null_mut(),
644+
)
645+
};
646+
647+
unsafe { CloseHandle(handle) };
648+
649+
if ret == 0 {
650+
let err = io::Error::last_os_error();
651+
// ERROR_INVALID_PARAMETER means not supported on this platform
652+
if err.raw_os_error() == Some(Foundation::ERROR_INVALID_PARAMETER as i32) {
653+
return Ok(false);
654+
}
655+
return Err(err.to_pyexception(vm));
656+
}
657+
658+
Ok((volume_state.volume_flags & PERSISTENT_VOLUME_STATE_DEV_VOLUME) != 0)
659+
}
660+
570661
// cwait is available on MSVC only
571662
#[cfg(target_env = "msvc")]
572663
unsafe extern "C" {

0 commit comments

Comments
 (0)