Skip to content

Commit 1fd8787

Browse files
authored
Merge pull request RustPython#2002 from BasixKOR/sys-getwindowsversion
Implement `sys.getwindowsversion`
2 parents 75b9108 + 240a31e commit 1fd8787

File tree

2 files changed

+104
-0
lines changed

2 files changed

+104
-0
lines changed

tests/snippets/stdlib_os.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,3 +491,26 @@ def __exit__(self, exc_type, exc_val, exc_tb):
491491

492492
for arg in [None, 1, 1.0, TabError]:
493493
assert_raises(TypeError, os.system, arg)
494+
495+
if sys.platform.startswith("win"):
496+
winver = sys.getwindowsversion()
497+
498+
# the biggest value of wSuiteMask (https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoexa#members).
499+
all_masks = 0x00000004 | 0x00000400 | 0x00004000 | 0x00000080 | 0x00000002 | 0x00000040 | 0x00000200 | \
500+
0x00000100 | 0x00000001 | 0x00000020 | 0x00002000 | 0x00000010 | 0x00008000 | 0x00020000
501+
502+
# We really can't test if the results are correct, so it just checks for meaningful value
503+
assert winver.major > 0
504+
assert winver.minor >= 0
505+
assert winver.build > 0
506+
assert winver.platform == 2
507+
assert isinstance(winver.service_pack, str)
508+
assert 0 <= winver.suite_mask <= all_masks
509+
assert 1 <= winver.product_type <= 3
510+
511+
# XXX if platform_version is implemented correctly, this'll break on compatiblity mode or a build without manifest
512+
assert winver.major == winver.platform_version[0]
513+
assert winver.minor == winver.platform_version[1]
514+
assert winver.build == winver.platform_version[2]
515+
516+

vm/src/sysmodule.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,78 @@ fn sys_displayhook(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
231231
Ok(())
232232
}
233233

234+
#[pystruct_sequence(module = "sys", name = "getwindowsversion")]
235+
#[derive(Default, Debug)]
236+
#[cfg(windows)]
237+
struct WindowsVersion {
238+
major: u32,
239+
minor: u32,
240+
build: u32,
241+
platform: u32,
242+
service_pack: String,
243+
service_pack_major: u16,
244+
service_pack_minor: u16,
245+
suite_mask: u16,
246+
product_type: u8,
247+
platform_version: (u32, u32, u32),
248+
}
249+
250+
#[cfg(windows)]
251+
fn sys_getwindowsversion(vm: &VirtualMachine) -> PyResult<crate::obj::objtuple::PyTupleRef> {
252+
use std::ffi::OsString;
253+
use std::os::windows::ffi::OsStringExt;
254+
use winapi::um::{
255+
sysinfoapi::GetVersionExW,
256+
winnt::{LPOSVERSIONINFOEXW, LPOSVERSIONINFOW, OSVERSIONINFOEXW},
257+
};
258+
259+
let mut version = OSVERSIONINFOEXW::default();
260+
version.dwOSVersionInfoSize = std::mem::size_of::<OSVERSIONINFOEXW>() as u32;
261+
let result = unsafe {
262+
let osvi = &mut version as LPOSVERSIONINFOEXW as LPOSVERSIONINFOW;
263+
// SAFE: GetVersionExW accepts a pointer of OSVERSIONINFOW, but winapi crate's type currently doesn't allow to do so.
264+
// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getversionexw#parameters
265+
GetVersionExW(osvi)
266+
};
267+
268+
if result == 0 {
269+
Err(vm.new_os_error("failed to get windows version".to_owned()))
270+
} else {
271+
let service_pack = {
272+
let (last, _) = version
273+
.szCSDVersion
274+
.iter()
275+
.take_while(|&x| x != &0)
276+
.enumerate()
277+
.last()
278+
.unwrap_or((0, &0));
279+
let sp = OsString::from_wide(&version.szCSDVersion[..last]);
280+
if let Ok(string) = sp.into_string() {
281+
string
282+
} else {
283+
return Err(vm.new_os_error("service pack is not ASCII".to_owned()));
284+
}
285+
};
286+
WindowsVersion {
287+
major: version.dwMajorVersion,
288+
minor: version.dwMinorVersion,
289+
build: version.dwBuildNumber,
290+
platform: version.dwPlatformId,
291+
service_pack,
292+
service_pack_major: version.wServicePackMajor,
293+
service_pack_minor: version.wServicePackMinor,
294+
suite_mask: version.wSuiteMask,
295+
product_type: version.wProductType,
296+
platform_version: (
297+
version.dwMajorVersion,
298+
version.dwMinorVersion,
299+
version.dwBuildNumber,
300+
), // TODO Provide accurate version, like CPython impl
301+
}
302+
.into_struct_sequence(vm, vm.try_class("sys", "_getwindowsversion_type")?)
303+
}
304+
}
305+
234306
const PLATFORM: &str = {
235307
cfg_if::cfg_if! {
236308
if #[cfg(any(target_os = "linux", target_os = "android"))] {
@@ -474,6 +546,15 @@ settrace() -- set the global debug tracing function
474546
"__displayhook__" => ctx.new_function(sys_displayhook),
475547
});
476548

549+
#[cfg(windows)]
550+
{
551+
let getwindowsversion = WindowsVersion::make_class(ctx);
552+
extend_module!(vm, module, {
553+
"getwindowsversion" => ctx.new_function(sys_getwindowsversion),
554+
"_getwindowsversion_type" => getwindowsversion, // XXX: This is not a python spec but required by current RustPython implementation
555+
})
556+
}
557+
477558
modules.set_item("sys", module.clone(), vm).unwrap();
478559
modules.set_item("builtins", builtins.clone(), vm).unwrap();
479560
}

0 commit comments

Comments
 (0)