Skip to content

Commit 2a50190

Browse files
committed
impl more msvcrt
1 parent fb3e132 commit 2a50190

File tree

3 files changed

+115
-6
lines changed

3 files changed

+115
-6
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: 73 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,62 @@ mod msvcrt {
7390
Ok(())
7491
}
7592

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

crates/vm/src/stdlib/winapi.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ mod _winapi {
4444
FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_TYPE_CHAR, FILE_TYPE_DISK, FILE_TYPE_PIPE,
4545
FILE_TYPE_REMOTE, FILE_TYPE_UNKNOWN, OPEN_EXISTING, PIPE_ACCESS_DUPLEX,
4646
PIPE_ACCESS_INBOUND, SYNCHRONIZE,
47+
// CreateFile constants
48+
CREATE_NEW, CREATE_ALWAYS, OPEN_ALWAYS, TRUNCATE_EXISTING,
49+
FILE_SHARE_READ, FILE_SHARE_WRITE, FILE_SHARE_DELETE,
50+
FILE_ATTRIBUTE_NORMAL, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_DELETE_ON_CLOSE,
51+
FILE_FLAG_NO_BUFFERING, FILE_FLAG_OPEN_REPARSE_POINT, FILE_FLAG_POSIX_SEMANTICS,
52+
FILE_FLAG_RANDOM_ACCESS, FILE_FLAG_SEQUENTIAL_SCAN, FILE_FLAG_WRITE_THROUGH,
4753
},
4854
System::{
4955
Console::{STD_ERROR_HANDLE, STD_INPUT_HANDLE, STD_OUTPUT_HANDLE},
@@ -78,6 +84,42 @@ mod _winapi {
7884
WindowsSysResult(unsafe { windows_sys::Win32::Foundation::CloseHandle(handle.0) })
7985
}
8086

87+
/// CreateFile - Create or open a file or I/O device.
88+
#[pyfunction]
89+
#[allow(clippy::too_many_arguments)]
90+
fn CreateFile(
91+
file_name: PyStrRef,
92+
desired_access: u32,
93+
share_mode: u32,
94+
_security_attributes: PyObjectRef, // Always NULL (0)
95+
creation_disposition: u32,
96+
flags_and_attributes: u32,
97+
_template_file: PyObjectRef, // Always NULL (0)
98+
vm: &VirtualMachine,
99+
) -> PyResult<WinHandle> {
100+
use windows_sys::Win32::Storage::FileSystem::CreateFileW;
101+
102+
let file_name_wide = file_name.as_wtf8().to_wide_with_nul();
103+
104+
let handle = unsafe {
105+
CreateFileW(
106+
file_name_wide.as_ptr(),
107+
desired_access,
108+
share_mode,
109+
null(),
110+
creation_disposition,
111+
flags_and_attributes,
112+
null_mut(),
113+
)
114+
};
115+
116+
if handle == INVALID_HANDLE_VALUE {
117+
return Err(vm.new_last_os_error());
118+
}
119+
120+
Ok(WinHandle(handle))
121+
}
122+
81123
#[pyfunction]
82124
fn GetStdHandle(
83125
std_handle: windows_sys::Win32::System::Console::STD_HANDLE,

0 commit comments

Comments
 (0)