mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
Add missing windows APIs
This commit is contained in:
@@ -139,7 +139,11 @@
|
||||
"birthtime",
|
||||
"IFEXEC",
|
||||
// "stat"
|
||||
"FIRMLINK"
|
||||
"FIRMLINK",
|
||||
// CPython internal names
|
||||
"sysdict",
|
||||
"settraceallthreads",
|
||||
"setprofileallthreads"
|
||||
],
|
||||
// flagWords - list of words to be always considered incorrect
|
||||
"flagWords": [
|
||||
|
||||
1
Lib/test/test_os.py
vendored
1
Lib/test/test_os.py
vendored
@@ -2773,7 +2773,6 @@ class Win32KillTests(unittest.TestCase):
|
||||
|
||||
self._kill_with_event(signal.CTRL_C_EVENT, "CTRL_C_EVENT")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@support.requires_subprocess()
|
||||
def test_CTRL_BREAK_EVENT(self):
|
||||
self._kill_with_event(signal.CTRL_BREAK_EVENT, "CTRL_BREAK_EVENT")
|
||||
|
||||
@@ -131,7 +131,10 @@ pub fn winerror_to_errno(winerror: i32) -> i32 {
|
||||
use libc::*;
|
||||
use windows_sys::Win32::{
|
||||
Foundation::*,
|
||||
Networking::WinSock::{WSAEACCES, WSAEBADF, WSAEFAULT, WSAEINTR, WSAEINVAL, WSAEMFILE},
|
||||
Networking::WinSock::{
|
||||
WSAEACCES, WSAEBADF, WSAECONNABORTED, WSAECONNREFUSED, WSAECONNRESET, WSAEFAULT,
|
||||
WSAEINTR, WSAEINVAL, WSAEMFILE,
|
||||
},
|
||||
};
|
||||
// Unwrap FACILITY_WIN32 HRESULT errors.
|
||||
// if ((winerror & 0xFFFF0000) == 0x80070000) {
|
||||
@@ -218,6 +221,11 @@ pub fn winerror_to_errno(winerror: i32) -> i32 {
|
||||
ERROR_BROKEN_PIPE | ERROR_NO_DATA => EPIPE,
|
||||
ERROR_DIR_NOT_EMPTY => ENOTEMPTY,
|
||||
ERROR_NO_UNICODE_TRANSLATION => EILSEQ,
|
||||
// Connection-related Windows error codes - map to Winsock error codes
|
||||
// which Python uses on Windows (errno.ECONNREFUSED = 10061, etc.)
|
||||
ERROR_CONNECTION_REFUSED => WSAECONNREFUSED,
|
||||
ERROR_CONNECTION_ABORTED => WSAECONNABORTED,
|
||||
ERROR_NETNAME_DELETED => WSAECONNRESET,
|
||||
ERROR_INVALID_FUNCTION
|
||||
| ERROR_INVALID_ACCESS
|
||||
| ERROR_INVALID_DATA
|
||||
|
||||
@@ -101,6 +101,14 @@ pub(crate) mod _signal {
|
||||
#[pyattr]
|
||||
pub use libc::{SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM};
|
||||
|
||||
// Windows-specific control events for GenerateConsoleCtrlEvent
|
||||
#[cfg(windows)]
|
||||
#[pyattr]
|
||||
const CTRL_C_EVENT: u32 = 0;
|
||||
#[cfg(windows)]
|
||||
#[pyattr]
|
||||
const CTRL_BREAK_EVENT: u32 = 1;
|
||||
|
||||
#[cfg(unix)]
|
||||
#[pyattr]
|
||||
use libc::{
|
||||
|
||||
@@ -6,15 +6,16 @@ pub(crate) use _winapi::module_def;
|
||||
#[pymodule]
|
||||
mod _winapi {
|
||||
use crate::{
|
||||
PyObjectRef, PyResult, TryFromObject, VirtualMachine,
|
||||
Py, PyObjectRef, PyPayload, PyResult, TryFromObject, VirtualMachine,
|
||||
builtins::PyStrRef,
|
||||
common::windows::ToWideString,
|
||||
common::{lock::PyMutex, windows::ToWideString},
|
||||
convert::{ToPyException, ToPyResult},
|
||||
function::{ArgMapping, ArgSequence, OptionalArg},
|
||||
types::Constructor,
|
||||
windows::{WinHandle, WindowsSysResult},
|
||||
};
|
||||
use std::ptr::{null, null_mut};
|
||||
use windows_sys::Win32::Foundation::{INVALID_HANDLE_VALUE, MAX_PATH};
|
||||
use windows_sys::Win32::Foundation::{HANDLE, INVALID_HANDLE_VALUE, MAX_PATH};
|
||||
|
||||
#[pyattr]
|
||||
use windows_sys::Win32::{
|
||||
@@ -557,6 +558,48 @@ mod _winapi {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn WaitForMultipleObjects(
|
||||
handle_seq: ArgSequence<isize>,
|
||||
wait_all: bool,
|
||||
milliseconds: u32,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<u32> {
|
||||
use windows_sys::Win32::Foundation::WAIT_FAILED;
|
||||
use windows_sys::Win32::System::Threading::WaitForMultipleObjects as WinWaitForMultipleObjects;
|
||||
|
||||
let handles: Vec<HANDLE> = handle_seq
|
||||
.into_vec()
|
||||
.into_iter()
|
||||
.map(|h| h as HANDLE)
|
||||
.collect();
|
||||
|
||||
if handles.is_empty() {
|
||||
return Err(vm.new_value_error("handle_seq must not be empty".to_owned()));
|
||||
}
|
||||
|
||||
if handles.len() > 64 {
|
||||
return Err(
|
||||
vm.new_value_error("WaitForMultipleObjects supports at most 64 handles".to_owned())
|
||||
);
|
||||
}
|
||||
|
||||
let ret = unsafe {
|
||||
WinWaitForMultipleObjects(
|
||||
handles.len() as u32,
|
||||
handles.as_ptr(),
|
||||
if wait_all { 1 } else { 0 },
|
||||
milliseconds,
|
||||
)
|
||||
};
|
||||
|
||||
if ret == WAIT_FAILED {
|
||||
Err(vm.new_last_os_error())
|
||||
} else {
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn GetExitCodeProcess(h: WinHandle, vm: &VirtualMachine) -> PyResult<u32> {
|
||||
unsafe {
|
||||
@@ -761,6 +804,229 @@ mod _winapi {
|
||||
Ok(WinHandle(handle))
|
||||
}
|
||||
|
||||
// ==================== Overlapped class ====================
|
||||
// Used for asynchronous I/O operations (ConnectNamedPipe, ReadFile, WriteFile)
|
||||
|
||||
#[pyattr]
|
||||
#[pyclass(name = "Overlapped", module = "_winapi")]
|
||||
#[derive(Debug, PyPayload)]
|
||||
struct Overlapped {
|
||||
inner: PyMutex<OverlappedInner>,
|
||||
}
|
||||
|
||||
struct OverlappedInner {
|
||||
overlapped: windows_sys::Win32::System::IO::OVERLAPPED,
|
||||
handle: HANDLE,
|
||||
pending: bool,
|
||||
completed: bool,
|
||||
read_buffer: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for OverlappedInner {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("OverlappedInner")
|
||||
.field("handle", &self.handle)
|
||||
.field("pending", &self.pending)
|
||||
.field("completed", &self.completed)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Sync for OverlappedInner {}
|
||||
unsafe impl Send for OverlappedInner {}
|
||||
|
||||
#[pyclass(with(Constructor))]
|
||||
impl Overlapped {
|
||||
fn new_with_handle(handle: HANDLE) -> Self {
|
||||
use windows_sys::Win32::System::Threading::CreateEventW;
|
||||
|
||||
let event = unsafe { CreateEventW(null(), 1, 0, null()) };
|
||||
let mut overlapped: windows_sys::Win32::System::IO::OVERLAPPED =
|
||||
unsafe { std::mem::zeroed() };
|
||||
overlapped.hEvent = event;
|
||||
|
||||
Overlapped {
|
||||
inner: PyMutex::new(OverlappedInner {
|
||||
overlapped,
|
||||
handle,
|
||||
pending: false,
|
||||
completed: false,
|
||||
read_buffer: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn GetOverlappedResult(&self, wait: bool, vm: &VirtualMachine) -> PyResult<u32> {
|
||||
use windows_sys::Win32::Foundation::{ERROR_IO_PENDING, GetLastError};
|
||||
use windows_sys::Win32::System::IO::GetOverlappedResult;
|
||||
|
||||
let mut inner = self.inner.lock();
|
||||
|
||||
// If operation was already completed synchronously (e.g., ERROR_PIPE_CONNECTED),
|
||||
// return immediately without calling GetOverlappedResult
|
||||
if inner.completed && !inner.pending {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let mut transferred: u32 = 0;
|
||||
|
||||
let ret = unsafe {
|
||||
GetOverlappedResult(
|
||||
inner.handle,
|
||||
&inner.overlapped,
|
||||
&mut transferred,
|
||||
if wait { 1 } else { 0 },
|
||||
)
|
||||
};
|
||||
|
||||
if ret == 0 {
|
||||
let err = unsafe { GetLastError() };
|
||||
if err == ERROR_IO_PENDING {
|
||||
return Err(std::io::Error::from_raw_os_error(err as i32).to_pyexception(vm));
|
||||
}
|
||||
return Err(std::io::Error::from_raw_os_error(err as i32).to_pyexception(vm));
|
||||
}
|
||||
|
||||
inner.completed = true;
|
||||
inner.pending = false;
|
||||
Ok(transferred)
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn getbuffer(&self, vm: &VirtualMachine) -> PyResult<Option<PyObjectRef>> {
|
||||
let inner = self.inner.lock();
|
||||
if !inner.completed {
|
||||
return Err(vm.new_value_error("operation not completed".to_owned()));
|
||||
}
|
||||
Ok(inner
|
||||
.read_buffer
|
||||
.as_ref()
|
||||
.map(|buf| vm.ctx.new_bytes(buf.clone()).into()))
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn cancel(&self, vm: &VirtualMachine) -> PyResult<()> {
|
||||
use windows_sys::Win32::System::IO::CancelIoEx;
|
||||
|
||||
let inner = self.inner.lock();
|
||||
if !inner.pending {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let ret = unsafe { CancelIoEx(inner.handle, &inner.overlapped) };
|
||||
if ret == 0 {
|
||||
let err = unsafe { windows_sys::Win32::Foundation::GetLastError() };
|
||||
// ERROR_NOT_FOUND means operation already completed
|
||||
if err != windows_sys::Win32::Foundation::ERROR_NOT_FOUND {
|
||||
return Err(std::io::Error::from_raw_os_error(err as i32).to_pyexception(vm));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn event(&self) -> isize {
|
||||
let inner = self.inner.lock();
|
||||
inner.overlapped.hEvent as isize
|
||||
}
|
||||
}
|
||||
|
||||
impl Constructor for Overlapped {
|
||||
type Args = ();
|
||||
|
||||
fn py_new(
|
||||
_cls: &Py<crate::builtins::PyType>,
|
||||
_args: Self::Args,
|
||||
_vm: &VirtualMachine,
|
||||
) -> PyResult<Self> {
|
||||
Ok(Overlapped::new_with_handle(null_mut()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for OverlappedInner {
|
||||
fn drop(&mut self) {
|
||||
use windows_sys::Win32::Foundation::CloseHandle;
|
||||
if !self.overlapped.hEvent.is_null() {
|
||||
unsafe { CloseHandle(self.overlapped.hEvent) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// ConnectNamedPipe - Wait for a client to connect to a named pipe
|
||||
#[derive(FromArgs)]
|
||||
struct ConnectNamedPipeArgs {
|
||||
#[pyarg(positional)]
|
||||
handle: WinHandle,
|
||||
#[pyarg(named, optional)]
|
||||
overlapped: OptionalArg<bool>,
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn ConnectNamedPipe(args: ConnectNamedPipeArgs, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
|
||||
use windows_sys::Win32::Foundation::{
|
||||
ERROR_IO_PENDING, ERROR_PIPE_CONNECTED, GetLastError,
|
||||
};
|
||||
|
||||
let handle = args.handle;
|
||||
let use_overlapped = args.overlapped.unwrap_or(false);
|
||||
|
||||
if use_overlapped {
|
||||
// Overlapped (async) mode
|
||||
let ov = Overlapped::new_with_handle(handle.0);
|
||||
|
||||
let ret = {
|
||||
let mut inner = ov.inner.lock();
|
||||
unsafe {
|
||||
windows_sys::Win32::System::Pipes::ConnectNamedPipe(
|
||||
handle.0,
|
||||
&mut inner.overlapped,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
if ret != 0 {
|
||||
// Connected immediately
|
||||
let mut inner = ov.inner.lock();
|
||||
inner.completed = true;
|
||||
} else {
|
||||
let err = unsafe { GetLastError() };
|
||||
match err {
|
||||
ERROR_IO_PENDING => {
|
||||
let mut inner = ov.inner.lock();
|
||||
inner.pending = true;
|
||||
}
|
||||
ERROR_PIPE_CONNECTED => {
|
||||
// Client was already connected
|
||||
let mut inner = ov.inner.lock();
|
||||
inner.completed = true;
|
||||
}
|
||||
_ => {
|
||||
return Err(
|
||||
std::io::Error::from_raw_os_error(err as i32).to_pyexception(vm)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ov.into_pyobject(vm))
|
||||
} else {
|
||||
// Synchronous mode
|
||||
let ret = unsafe {
|
||||
windows_sys::Win32::System::Pipes::ConnectNamedPipe(handle.0, null_mut())
|
||||
};
|
||||
|
||||
if ret == 0 {
|
||||
let err = unsafe { GetLastError() };
|
||||
if err != ERROR_PIPE_CONNECTED {
|
||||
return Err(std::io::Error::from_raw_os_error(err as i32).to_pyexception(vm));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(vm.ctx.none())
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper for GetShortPathName and GetLongPathName
|
||||
fn get_path_name_impl(
|
||||
path: &PyStrRef,
|
||||
|
||||
Reference in New Issue
Block a user