Skip to content

Commit 26d64b9

Browse files
committed
impl more msvcrt
1 parent 1251fbf commit 26d64b9

File tree

3 files changed

+158
-19
lines changed

3 files changed

+158
-19
lines changed

Lib/test/test_msvcrt.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,13 @@
1515

1616

1717
class TestFileOperations(unittest.TestCase):
18-
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'msvcrt' has no attribute 'locking'
1918
def test_locking(self):
2019
with open(TESTFN, "w") as f:
2120
self.addCleanup(os_helper.unlink, TESTFN)
2221

2322
msvcrt.locking(f.fileno(), msvcrt.LK_LOCK, 1)
2423
self.assertRaises(OSError, msvcrt.locking, f.fileno(), msvcrt.LK_NBLCK, 1)
2524

26-
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'msvcrt' has no attribute 'locking'
2725
def test_unlockfile(self):
2826
with open(TESTFN, "w") as f:
2927
self.addCleanup(os_helper.unlink, TESTFN)
@@ -39,7 +37,6 @@ def test_setmode(self):
3937
msvcrt.setmode(f.fileno(), os.O_BINARY)
4038
msvcrt.setmode(f.fileno(), os.O_TEXT)
4139

42-
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module '_winapi' has no attribute 'CreateFile'. Did you mean: 'CreatePipe'?
4340
def test_open_osfhandle(self):
4441
h = _winapi.CreateFile(TESTFN_ASCII, _winapi.GENERIC_WRITE, 0, 0, 1, 128, 0)
4542
self.addCleanup(os_helper.unlink, TESTFN_ASCII)
@@ -80,7 +77,6 @@ def test_kbhit(self):
8077
''')
8178
self.run_in_separated_process(code)
8279

83-
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'msvcrt' has no attribute 'ungetch'. Did you mean: 'getch'?
8480
def test_getch(self):
8581
msvcrt.ungetch(b'c')
8682
self.assertEqual(msvcrt.getch(), b'c')
@@ -98,7 +94,6 @@ def check_getwch(self, funcname):
9894
def test_getwch(self):
9995
self.check_getwch('getwch')
10096

101-
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'msvcrt' has no attribute 'ungetch'. Did you mean: 'getch'?
10297
def test_getche(self):
10398
msvcrt.ungetch(b'c')
10499
self.assertEqual(msvcrt.getche(), b'c')
@@ -114,7 +109,6 @@ def test_putwch(self):
114109

115110

116111
class TestOther(unittest.TestCase):
117-
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'msvcrt' has no attribute 'heapmin'
118112
def test_heap_min(self):
119113
try:
120114
msvcrt.heapmin()

crates/vm/src/stdlib/msvcrt.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,25 @@ mod msvcrt {
3131
fn _getwche() -> u32;
3232
fn _putch(c: u32) -> i32;
3333
fn _putwch(c: u16) -> u32;
34+
fn _ungetch(c: i32) -> i32;
35+
fn _ungetwch(c: u32) -> u32;
36+
fn _locking(fd: i32, mode: i32, nbytes: i64) -> i32;
37+
fn _heapmin() -> i32;
38+
fn _kbhit() -> i32;
3439
}
3540

41+
// Locking mode constants
42+
#[pyattr]
43+
const LK_UNLCK: i32 = 0; // Unlock
44+
#[pyattr]
45+
const LK_LOCK: i32 = 1; // Lock (blocking)
46+
#[pyattr]
47+
const LK_NBLCK: i32 = 2; // Non-blocking lock
48+
#[pyattr]
49+
const LK_RLCK: i32 = 3; // Lock for reading (same as LK_LOCK)
50+
#[pyattr]
51+
const LK_NBRLCK: i32 = 4; // Non-blocking lock for reading (same as LK_NBLCK)
52+
3653
#[pyfunction]
3754
fn getch() -> Vec<u8> {
3855
let c = unsafe { _getch() };
@@ -73,6 +90,60 @@ mod msvcrt {
7390
Ok(())
7491
}
7592

93+
#[pyfunction]
94+
fn ungetch(b: PyRef<PyBytes>, vm: &VirtualMachine) -> PyResult<()> {
95+
let &c = b.as_bytes().iter().exactly_one().map_err(|_| {
96+
vm.new_type_error("ungetch() argument must be a byte string of length 1")
97+
})?;
98+
let ret = unsafe { suppress_iph!(_ungetch(c as i32)) };
99+
if ret == -1 {
100+
// EOF returned means the buffer is full
101+
Err(vm.new_os_error(libc::ENOSPC))
102+
} else {
103+
Ok(())
104+
}
105+
}
106+
107+
#[pyfunction]
108+
fn ungetwch(s: PyStrRef, vm: &VirtualMachine) -> PyResult<()> {
109+
let c =
110+
s.as_str().chars().exactly_one().map_err(|_| {
111+
vm.new_type_error("ungetwch() argument must be a string of length 1")
112+
})?;
113+
let ret = unsafe { suppress_iph!(_ungetwch(c as u32)) };
114+
if ret == 0xFFFF {
115+
// WEOF returned means the buffer is full
116+
Err(vm.new_os_error(libc::ENOSPC))
117+
} else {
118+
Ok(())
119+
}
120+
}
121+
122+
#[pyfunction]
123+
fn kbhit() -> i32 {
124+
unsafe { _kbhit() }
125+
}
126+
127+
#[pyfunction]
128+
fn locking(fd: i32, mode: i32, nbytes: i64, vm: &VirtualMachine) -> PyResult<()> {
129+
let ret = unsafe { suppress_iph!(_locking(fd, mode, nbytes)) };
130+
if ret == -1 {
131+
Err(vm.new_last_errno_error())
132+
} else {
133+
Ok(())
134+
}
135+
}
136+
137+
#[pyfunction]
138+
fn heapmin(vm: &VirtualMachine) -> PyResult<()> {
139+
let ret = unsafe { suppress_iph!(_heapmin()) };
140+
if ret == -1 {
141+
Err(vm.new_last_errno_error())
142+
} else {
143+
Ok(())
144+
}
145+
}
146+
76147
unsafe extern "C" {
77148
fn _setmode(fd: crt_fd::Borrowed<'_>, flags: i32) -> i32;
78149
}

crates/vm/src/stdlib/winapi.rs

Lines changed: 87 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,57 @@ mod _winapi {
3131
LCMAP_TRADITIONAL_CHINESE, LCMAP_UPPERCASE,
3232
},
3333
Storage::FileSystem::{
34-
COPY_FILE_ALLOW_DECRYPTED_DESTINATION, COPY_FILE_COPY_SYMLINK,
35-
COPY_FILE_FAIL_IF_EXISTS, COPY_FILE_NO_BUFFERING, COPY_FILE_NO_OFFLOAD,
36-
COPY_FILE_OPEN_SOURCE_FOR_WRITE, COPY_FILE_REQUEST_COMPRESSED_TRAFFIC,
37-
COPY_FILE_REQUEST_SECURITY_PRIVILEGES, COPY_FILE_RESTARTABLE,
38-
COPY_FILE_RESUME_FROM_PAUSE, COPYFILE2_CALLBACK_CHUNK_FINISHED,
39-
COPYFILE2_CALLBACK_CHUNK_STARTED, COPYFILE2_CALLBACK_ERROR,
40-
COPYFILE2_CALLBACK_POLL_CONTINUE, COPYFILE2_CALLBACK_STREAM_FINISHED,
41-
COPYFILE2_CALLBACK_STREAM_STARTED, COPYFILE2_PROGRESS_CANCEL,
42-
COPYFILE2_PROGRESS_CONTINUE, COPYFILE2_PROGRESS_PAUSE, COPYFILE2_PROGRESS_QUIET,
43-
COPYFILE2_PROGRESS_STOP, FILE_FLAG_FIRST_PIPE_INSTANCE, FILE_FLAG_OVERLAPPED,
44-
FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_TYPE_CHAR, FILE_TYPE_DISK, FILE_TYPE_PIPE,
45-
FILE_TYPE_REMOTE, FILE_TYPE_UNKNOWN, OPEN_EXISTING, PIPE_ACCESS_DUPLEX,
46-
PIPE_ACCESS_INBOUND, SYNCHRONIZE,
34+
COPY_FILE_ALLOW_DECRYPTED_DESTINATION,
35+
COPY_FILE_COPY_SYMLINK,
36+
COPY_FILE_FAIL_IF_EXISTS,
37+
COPY_FILE_NO_BUFFERING,
38+
COPY_FILE_NO_OFFLOAD,
39+
COPY_FILE_OPEN_SOURCE_FOR_WRITE,
40+
COPY_FILE_REQUEST_COMPRESSED_TRAFFIC,
41+
COPY_FILE_REQUEST_SECURITY_PRIVILEGES,
42+
COPY_FILE_RESTARTABLE,
43+
COPY_FILE_RESUME_FROM_PAUSE,
44+
COPYFILE2_CALLBACK_CHUNK_FINISHED,
45+
COPYFILE2_CALLBACK_CHUNK_STARTED,
46+
COPYFILE2_CALLBACK_ERROR,
47+
COPYFILE2_CALLBACK_POLL_CONTINUE,
48+
COPYFILE2_CALLBACK_STREAM_FINISHED,
49+
COPYFILE2_CALLBACK_STREAM_STARTED,
50+
COPYFILE2_PROGRESS_CANCEL,
51+
COPYFILE2_PROGRESS_CONTINUE,
52+
COPYFILE2_PROGRESS_PAUSE,
53+
COPYFILE2_PROGRESS_QUIET,
54+
COPYFILE2_PROGRESS_STOP,
55+
CREATE_ALWAYS,
56+
// CreateFile constants
57+
CREATE_NEW,
58+
FILE_ATTRIBUTE_NORMAL,
59+
FILE_FLAG_BACKUP_SEMANTICS,
60+
FILE_FLAG_DELETE_ON_CLOSE,
61+
FILE_FLAG_FIRST_PIPE_INSTANCE,
62+
FILE_FLAG_NO_BUFFERING,
63+
FILE_FLAG_OPEN_REPARSE_POINT,
64+
FILE_FLAG_OVERLAPPED,
65+
FILE_FLAG_POSIX_SEMANTICS,
66+
FILE_FLAG_RANDOM_ACCESS,
67+
FILE_FLAG_SEQUENTIAL_SCAN,
68+
FILE_FLAG_WRITE_THROUGH,
69+
FILE_GENERIC_READ,
70+
FILE_GENERIC_WRITE,
71+
FILE_SHARE_DELETE,
72+
FILE_SHARE_READ,
73+
FILE_SHARE_WRITE,
74+
FILE_TYPE_CHAR,
75+
FILE_TYPE_DISK,
76+
FILE_TYPE_PIPE,
77+
FILE_TYPE_REMOTE,
78+
FILE_TYPE_UNKNOWN,
79+
OPEN_ALWAYS,
80+
OPEN_EXISTING,
81+
PIPE_ACCESS_DUPLEX,
82+
PIPE_ACCESS_INBOUND,
83+
SYNCHRONIZE,
84+
TRUNCATE_EXISTING,
4785
},
4886
System::{
4987
Console::{STD_ERROR_HANDLE, STD_INPUT_HANDLE, STD_OUTPUT_HANDLE},
@@ -78,6 +116,42 @@ mod _winapi {
78116
WindowsSysResult(unsafe { windows_sys::Win32::Foundation::CloseHandle(handle.0) })
79117
}
80118

119+
/// CreateFile - Create or open a file or I/O device.
120+
#[pyfunction]
121+
#[allow(clippy::too_many_arguments)]
122+
fn CreateFile(
123+
file_name: PyStrRef,
124+
desired_access: u32,
125+
share_mode: u32,
126+
_security_attributes: PyObjectRef, // Always NULL (0)
127+
creation_disposition: u32,
128+
flags_and_attributes: u32,
129+
_template_file: PyObjectRef, // Always NULL (0)
130+
vm: &VirtualMachine,
131+
) -> PyResult<WinHandle> {
132+
use windows_sys::Win32::Storage::FileSystem::CreateFileW;
133+
134+
let file_name_wide = file_name.as_wtf8().to_wide_with_nul();
135+
136+
let handle = unsafe {
137+
CreateFileW(
138+
file_name_wide.as_ptr(),
139+
desired_access,
140+
share_mode,
141+
null(),
142+
creation_disposition,
143+
flags_and_attributes,
144+
null_mut(),
145+
)
146+
};
147+
148+
if handle == INVALID_HANDLE_VALUE {
149+
return Err(vm.new_last_os_error());
150+
}
151+
152+
Ok(WinHandle(handle))
153+
}
154+
81155
#[pyfunction]
82156
fn GetStdHandle(
83157
std_handle: windows_sys::Win32::System::Console::STD_HANDLE,

0 commit comments

Comments
 (0)