Add per-type vectorcall for builtin constructors#7407
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (3)
📒 Files selected for processing (4)
📝 WalkthroughWalkthroughThe PR makes targeted adjustments to error handling, Windows API behavior, and type instantiation control. Changes include reorienting mmap error reporting to use OS errors, handling empty Windows environment mappings with proper NUL termination, removing deprecated WinSock error codes, and preventing instantiation of the Windows version class through a TypeError slot. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 4❌ Failed checks (4 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Sorry, something went wrong.
📦 Library DependenciesThe following Lib/ modules were modified. Here are their dependencies: [ ] test: cpython/Lib/test/test_mmap.py (TODO: 1) dependencies: dependent tests: (2 tests)
[x] lib: cpython/Lib/subprocess.py dependencies:
dependent tests: (81 tests)
[x] test: cpython/Lib/test/test_sys.py (TODO: 7) dependencies: dependent tests: (225 tests)
Legend:
|
Sorry, something went wrong.
2b764a2 to
a9ee2e3
Compare
March 17, 2026 11:38
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
crates/vm/src/builtins/float.rs (1)
528-538: Extract a shared vectorcall-to-slot_newhelper to avoid drift.
Line 528-Line 538duplicates the same trampoline used by multiple builtins. Centralizing this in one helper would keep behavior consistent and lower maintenance risk.Proposed refactor sketch
+// e.g. in a shared builtins/type helper module +pub(crate) fn vectorcall_via_slot_new( + zelf_obj: &PyObject, + args: Vec<PyObjectRef>, + nargs: usize, + kwnames: Option<&[PyObjectRef]>, + vm: &VirtualMachine, +) -> PyResult { + let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap(); + let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames); + (zelf.slots.new.load().unwrap())(zelf.to_owned(), func_args, vm) +}Then each builtin can just store that helper in its
slots.vectorcall.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/vm/src/builtins/float.rs` around lines 528 - 538, The vectorcall_float trampoline duplicates the pattern of converting vectorcall args to FuncArgs and invoking the type's slot_new (see vectorcall_float, FuncArgs::from_vectorcall_owned, and (zelf.slots.new.load().unwrap())(zelf.to_owned(), func_args, vm)); extract that logic into a single reusable helper (e.g., a function that accepts zelf_obj, args, nargs, kwnames, vm and performs the downcast, FuncArgs conversion, and calls slot_new) and have builtins set their slots.vectorcall to point to this helper to avoid drift and ensure consistent behavior across types.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@crates/vm/src/builtins/dict.rs`:
- Around line 1438-1449: The vectorcall_dict fast-path constructs a base dict
with PyDict::default().into_ref_with_type(...) and PyDict::slot_init(...)
without invoking the type's constructor hooks, so it must detect when the
receiver type has custom new/init hooks and fall back to the generic
PyType::call path; update vectorcall_dict to check the target type's slots (e.g.
inspect zelf.slots.new or zelf.slots.init or whether the type is exactly dict)
and if non-default hooks exist (or the type != built-in dict) call
PyType::call(zelf_obj, args, vm) (or the equivalent generic constructor path)
instead of the fast default+slot_init; apply the same pattern to the analogous
fast-paths in list.rs and bytearray.rs to preserve proper subclass constructor
semantics.
In `@crates/vm/src/builtins/tuple.rs`:
- Around line 698-709: The vectorcall_tuple implementation uses the type's
slot_new but returns immediately, skipping any subtype slot_init; update
vectorcall_tuple to mirror PyType::call by invoking the type's slots.new
(zelf.slots.new) to create the instance, then if the type has a non-None
slots.init (zelf.slots.init.load().is_some()) call that init slot with the newly
created object and the original FuncArgs (func_args) and vm before returning the
instance; do the same fix in the bytes vectorcall implementation to ensure
Rust-defined subtypes receive their post-new initializer.
In `@crates/vm/src/builtins/type.rs`:
- Around line 2668-2674: The branch currently dispatches an inherited vectorcall
when zelf.slots.call is None but doesn't check whether slots.new allows
instantiation; update the condition that calls type_vc so it also requires
zelf.slots.new.load().is_some() (i.e., only run type_vc(zelf_obj, args, nargs,
kwnames, vm) when vectorcall is Some AND slots.new is Some) to prevent
dispatching constructors for DISALLOW_INSTANTIATION types; alternatively, when
implementing set_new() for DISALLOW_INSTANTIATION ensure it clears
slots.vectorcall alongside setting slots.new to None so the invariant (no
vectorcall if no new) holds.
In `@crates/vm/src/types/slot.rs`:
- Around line 828-833: The inherit_from_mro handling for SlotAccessor::TpInit
(and TpNew) must also restore the vectorcall slot when the primary slot is
deleted: mirror the TpCall logic by copying the ancestor's vectorcall into
self.slots.vectorcall in the non-ADD (deletion) path so heap types regain
fast-path calls; locate the TpInit/TpNew cases (where update_main_slot! is used
for init/init_wrapper/Init) and add the same vectorcall restoration that TpCall
performs in inherit_from_mro.
---
Nitpick comments:
In `@crates/vm/src/builtins/float.rs`:
- Around line 528-538: The vectorcall_float trampoline duplicates the pattern of
converting vectorcall args to FuncArgs and invoking the type's slot_new (see
vectorcall_float, FuncArgs::from_vectorcall_owned, and
(zelf.slots.new.load().unwrap())(zelf.to_owned(), func_args, vm)); extract that
logic into a single reusable helper (e.g., a function that accepts zelf_obj,
args, nargs, kwnames, vm and performs the downcast, FuncArgs conversion, and
calls slot_new) and have builtins set their slots.vectorcall to point to this
helper to avoid drift and ensure consistent behavior across types.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: 81cce507-bbb7-45af-85bc-bfd1b573eedf
📒 Files selected for processing (14)
crates/vm/src/builtins/bool.rscrates/vm/src/builtins/bytearray.rscrates/vm/src/builtins/bytes.rscrates/vm/src/builtins/complex.rscrates/vm/src/builtins/dict.rscrates/vm/src/builtins/float.rscrates/vm/src/builtins/int.rscrates/vm/src/builtins/list.rscrates/vm/src/builtins/set.rscrates/vm/src/builtins/str.rscrates/vm/src/builtins/tuple.rscrates/vm/src/builtins/type.rscrates/vm/src/stdlib/os.rscrates/vm/src/types/slot.rs
Sorry, something went wrong.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
crates/vm/src/builtins/tuple.rs (1)
698-709: ⚠️ Potential issue | 🟠 Major
vectorcall_tupleskips subtypeslot_initafterslot_new.This path returns right after
slot_new, so subtype init hooks are bypassed. It should mirrorPyType::call’snew→initsequence when applicable.💡 Suggested fix
fn vectorcall_tuple( zelf_obj: &PyObject, args: Vec<PyObjectRef>, nargs: usize, kwnames: Option<&[PyObjectRef]>, vm: &VirtualMachine, ) -> PyResult { let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap(); let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames); - // Use the type's own slot_new rather than calling PyTuple::slot_new directly, - // so Rust-level subclasses (e.g. struct sequences) get their custom slot_new called. - (zelf.slots.new.load().unwrap())(zelf.to_owned(), func_args, vm) + let obj = (zelf.slots.new.load().unwrap())(zelf.to_owned(), func_args.clone(), vm)?; + if obj.class().fast_issubclass(zelf) { + if let Some(init_method) = obj.class().slots.init.load() { + init_method(obj.clone(), func_args, vm)?; + } + } + Ok(obj) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/vm/src/builtins/tuple.rs` around lines 698 - 709, vectorcall_tuple currently calls the type's slot_new and returns immediately, skipping any subclass slot_init; update vectorcall_tuple to mirror PyType::call's new→init sequence: after invoking (zelf.slots.new.load().unwrap()) and obtaining the created object, check whether the type has a non-default slot_init (e.g., via zelf.slots.init.load() or the same mechanism used in PyType::call), and if so call that init slot with the same FuncArgs and vm; propagate any error from slot_init and return the created instance on success, ensuring ownership/borrowing follows the existing pattern (use zelf.to_owned() and the created PyObjectRef as in the surrounding code).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@crates/vm/src/stdlib/os.rs`:
- Around line 1378-1379: The code that populates the
st_atime_ns/st_mtime_ns/st_ctime_ns fields is assigning raw second values
(items[i], etc.) directly (see vm.is_none and items usage), but the *_ns
contract expects nanoseconds; change the assignment so when the corresponding
*_ns slot (e.g., items[i+?] where st_*_ns are filled) is missing you assign the
seconds value multiplied by 1_000_000_000 (i.e., seconds * 1_000_000_000) rather
than the raw seconds clone; preserve the numeric type used elsewhere and ensure
you perform the multiplication before assigning to items[...] for st_atime_ns,
st_mtime_ns and st_ctime_ns.
---
Duplicate comments:
In `@crates/vm/src/builtins/tuple.rs`:
- Around line 698-709: vectorcall_tuple currently calls the type's slot_new and
returns immediately, skipping any subclass slot_init; update vectorcall_tuple to
mirror PyType::call's new→init sequence: after invoking
(zelf.slots.new.load().unwrap()) and obtaining the created object, check whether
the type has a non-default slot_init (e.g., via zelf.slots.init.load() or the
same mechanism used in PyType::call), and if so call that init slot with the
same FuncArgs and vm; propagate any error from slot_init and return the created
instance on success, ensuring ownership/borrowing follows the existing pattern
(use zelf.to_owned() and the created PyObjectRef as in the surrounding code).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: ef159242-c3e7-44ca-9737-7266cebe3ec4
📒 Files selected for processing (13)
crates/vm/src/builtins/bool.rscrates/vm/src/builtins/bytearray.rscrates/vm/src/builtins/dict.rscrates/vm/src/builtins/float.rscrates/vm/src/builtins/int.rscrates/vm/src/builtins/list.rscrates/vm/src/builtins/set.rscrates/vm/src/builtins/str.rscrates/vm/src/builtins/tuple.rscrates/vm/src/builtins/type.rscrates/vm/src/stdlib/os.rscrates/vm/src/types/slot.rscrates/vm/src/types/slot_defs.rs
🚧 Files skipped from review as they are similar to previous changes (6)
- crates/vm/src/builtins/list.rs
- crates/vm/src/builtins/int.rs
- crates/vm/src/builtins/bytearray.rs
- crates/vm/src/builtins/bool.rs
- crates/vm/src/builtins/set.rs
- crates/vm/src/builtins/type.rs
Sorry, something went wrong.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (3)
crates/vm/src/stdlib/os.rs (1)
1371-1379: ⚠️ Potential issue | 🟠 Major
st_*_nsfallback handling is missing.Line 1374 leaves nanosecond fields unset when omitted. That can break
st_atime_ns/st_mtime_ns/st_ctime_nsconsistency for sequence-based construction.💡 Suggested fix
for i in 7..=9 { if vm.is_none(&items[i + 3]) { items[i + 3] = items[i].clone(); } + if vm.is_none(&items[i + 6]) { + let billion: PyObjectRef = vm.ctx.new_int(1_000_000_000).into(); + items[i + 6] = vm._mul(&items[i], &billion)?; + } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/vm/src/stdlib/os.rs` around lines 1371 - 1379, The loop that copies integer time fields to float timestamp slots (for i in 7..=9) neglects to set the corresponding nanosecond slots (items[i+6]) when they are omitted; update the loop in the same block to check vm.is_none(&items[i + 6]) and, if missing, set items[i + 6] to a zero-nanoseconds value (e.g., an integer zero) so st_atime_ns/st_mtime_ns/st_ctime_ns are populated consistently alongside the float timestamps and integer seconds.crates/vm/src/builtins/list.rs (1)
771-782: ⚠️ Potential issue | 🟠 MajorThis fast path also skips subtype
slot_new.
PyList::default().into_ref_with_type(...)has the same hazard asdict: a Rust-definedlistsubtype can inherit this vectorcall and be allocated as a plainPyListinstead of going through its own constructor path. Please guard this fast path on the builtinlistslots or fall back to the generic type call path.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/vm/src/builtins/list.rs` around lines 771 - 782, The fast path in vectorcall_list incorrectly constructs a PyList for arbitrary subtypes via PyList::default().into_ref_with_type(...), skipping subtype construction; change vectorcall_list to only take the fast path when the target type is the exact builtin list type or when its new/slot_new is the builtin implementation (i.e., check the type against vm.ctx.types.list_type or compare its slot_new to the builtin slot), and otherwise fall back to the generic type call path so subclass constructors (slot_new) run; keep references to vectorcall_list, PyList::default().into_ref_with_type, PyList::slot_init and slot_new to locate and update the logic.crates/vm/src/builtins/dict.rs (1)
1438-1449: ⚠️ Potential issue | 🟠 MajorThis still bypasses subtype
slot_new.
PyDict::default().into_ref_with_type(...)never consults the target type’s own constructor slots, so a Rust-defineddictsubtype can inherit this vectorcall and skip its allocator/init hooks here. Please fall back to the generic type call path unless the target is still usingdict’s builtinnew/initslots.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/vm/src/builtins/dict.rs` around lines 1438 - 1449, vectorcall_dict currently creates an instance with PyDict::default().into_ref_with_type(...) which bypasses the target type’s own constructor slots; change it to first detect whether the target type still uses dict’s builtin new/init slots (i.e., compare the target type’s slot_new/slot_init to PyDict’s defaults or check type identity), and only use PyDict::default().into_ref_with_type + PyDict::slot_init when they match; otherwise fall back to the generic type call path (invoke the type’s normal call/constructor path via the VM so overridden alloc/init run) instead of instantiating directly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@crates/vm/src/types/slot_defs.rs`:
- Around line 456-469: The current inheritance code gates copying a parent
type's constructor fast-path behind the presence of an instance call slot (it
checks cls.slots.call before taking cls.slots.vectorcall), which prevents
subclasses of builtins (e.g., float/list) from inheriting constructor
vectorcall; change the find_map closure used to compute inherited_vc to
unconditionally return cls.slots.vectorcall.load() (i.e., remove the if
cls.slots.call.load().is_some() branch) so
typ.slots.vectorcall.store(inherited_vc) can copy constructor vectorcall
regardless of the parent's slots.call presence; apply the same removal of the
call-slot gating in the corresponding copy/inheritance site that sets vectorcall
(the symmetric code that assigns typ.slots.vectorcall).
---
Duplicate comments:
In `@crates/vm/src/builtins/dict.rs`:
- Around line 1438-1449: vectorcall_dict currently creates an instance with
PyDict::default().into_ref_with_type(...) which bypasses the target type’s own
constructor slots; change it to first detect whether the target type still uses
dict’s builtin new/init slots (i.e., compare the target type’s
slot_new/slot_init to PyDict’s defaults or check type identity), and only use
PyDict::default().into_ref_with_type + PyDict::slot_init when they match;
otherwise fall back to the generic type call path (invoke the type’s normal
call/constructor path via the VM so overridden alloc/init run) instead of
instantiating directly.
In `@crates/vm/src/builtins/list.rs`:
- Around line 771-782: The fast path in vectorcall_list incorrectly constructs a
PyList for arbitrary subtypes via PyList::default().into_ref_with_type(...),
skipping subtype construction; change vectorcall_list to only take the fast path
when the target type is the exact builtin list type or when its new/slot_new is
the builtin implementation (i.e., check the type against vm.ctx.types.list_type
or compare its slot_new to the builtin slot), and otherwise fall back to the
generic type call path so subclass constructors (slot_new) run; keep references
to vectorcall_list, PyList::default().into_ref_with_type, PyList::slot_init and
slot_new to locate and update the logic.
In `@crates/vm/src/stdlib/os.rs`:
- Around line 1371-1379: The loop that copies integer time fields to float
timestamp slots (for i in 7..=9) neglects to set the corresponding nanosecond
slots (items[i+6]) when they are omitted; update the loop in the same block to
check vm.is_none(&items[i + 6]) and, if missing, set items[i + 6] to a
zero-nanoseconds value (e.g., an integer zero) so
st_atime_ns/st_mtime_ns/st_ctime_ns are populated consistently alongside the
float timestamps and integer seconds.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: 1c7adec1-9b97-4816-a120-0a8b7fb9c5d7
📒 Files selected for processing (12)
crates/vm/src/builtins/bool.rscrates/vm/src/builtins/dict.rscrates/vm/src/builtins/float.rscrates/vm/src/builtins/int.rscrates/vm/src/builtins/list.rscrates/vm/src/builtins/set.rscrates/vm/src/builtins/str.rscrates/vm/src/builtins/tuple.rscrates/vm/src/builtins/type.rscrates/vm/src/stdlib/os.rscrates/vm/src/types/slot.rscrates/vm/src/types/slot_defs.rs
🚧 Files skipped from review as they are similar to previous changes (4)
- crates/vm/src/builtins/int.rs
- crates/vm/src/builtins/type.rs
- crates/vm/src/builtins/tuple.rs
- crates/vm/src/types/slot.rs
Sorry, something went wrong.
Add vectorcall fast paths for dict, list, set, int, float, str, bool, tuple, frozenset. Clear vectorcall when __init__/__new__ is overridden in Python. Prevent constructor vectorcall inheritance to heap subclasses. Fix stat_result to use struct_sequence_new with reference-copy for hidden time fields.
There was a problem hiding this comment.
♻️ Duplicate comments (1)
crates/vm/src/types/slot_defs.rs (1)
462-468: ⚠️ Potential issue | 🟠 MajorConstructor
vectorcallinheritance is blocked for non-callable builtins.Line 462 and Line 590 gate
slots.vectorcallinheritance onslots.call. That prevents subclasses of constructor-only builtins (dict,list,int, etc.) from inheriting constructor vectorcall and forces fallback to generic construction (crates/vm/src/builtins/type.rs, Line 2686).Suggested fix
Self::TpCall => { inherit_main!(call); - let inherited_vc = mro.iter().find_map(|cls| { - if cls.slots.call.load().is_some() { - cls.slots.vectorcall.load() - } else { - None - } - }); + let inherited_vc = mro.iter().find_map(|cls| cls.slots.vectorcall.load()); typ.slots.vectorcall.store(inherited_vc); }Self::TpCall => { copy_main!(call); - if typ.slots.vectorcall.load().is_none() - && base.slots.call.load().is_some() - && let Some(base_val) = base.slots.vectorcall.load() + if typ.slots.vectorcall.load().is_none() + && let Some(base_val) = base.slots.vectorcall.load() { typ.slots.vectorcall.store(Some(base_val)); } }Also applies to: 590-594
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/vm/src/types/slot_defs.rs` around lines 462 - 468, The code currently prevents inheriting slots.vectorcall when slots.call is absent (see the inherited_vc computation using mro.iter().find_map), which blocks constructor-only builtins from inheriting vectorcall; change the predicate to check only for the presence of cls.slots.vectorcall (not cls.slots.call) so vectorcall is inherited independently of call. Apply the same fix to the other similar block that conditions vectorcall on call (the second mro.iter().find_map instance around slots.vectorcall) so subclasses of constructor-only builtins can inherit constructor vectorcall.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@crates/vm/src/types/slot_defs.rs`:
- Around line 462-468: The code currently prevents inheriting slots.vectorcall
when slots.call is absent (see the inherited_vc computation using
mro.iter().find_map), which blocks constructor-only builtins from inheriting
vectorcall; change the predicate to check only for the presence of
cls.slots.vectorcall (not cls.slots.call) so vectorcall is inherited
independently of call. Apply the same fix to the other similar block that
conditions vectorcall on call (the second mro.iter().find_map instance around
slots.vectorcall) so subclasses of constructor-only builtins can inherit
constructor vectorcall.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: 348b0c7b-19d8-453b-91ae-a97e06e7c799
📒 Files selected for processing (12)
crates/vm/src/builtins/bool.rscrates/vm/src/builtins/dict.rscrates/vm/src/builtins/float.rscrates/vm/src/builtins/int.rscrates/vm/src/builtins/list.rscrates/vm/src/builtins/set.rscrates/vm/src/builtins/str.rscrates/vm/src/builtins/tuple.rscrates/vm/src/builtins/type.rscrates/vm/src/stdlib/os.rscrates/vm/src/types/slot.rscrates/vm/src/types/slot_defs.rs
🚧 Files skipped from review as they are similar to previous changes (5)
- crates/vm/src/builtins/bool.rs
- crates/vm/src/builtins/list.rs
- crates/vm/src/builtins/str.rs
- crates/vm/src/types/slot.rs
- crates/vm/src/builtins/tuple.rs
Sorry, something went wrong.
ff165a2 to
f76e0d1
Compare
March 17, 2026 14:16
62edc47
into
RustPython:main
Mar 18, 2026
Add vectorcall fast paths for dict, list, set, int, float, str, bool, tuple, frozenset. Clear vectorcall when __init__/__new__ is overridden in Python. Prevent constructor vectorcall inheritance to heap subclasses. Fix stat_result to use struct_sequence_new with reference-copy for hidden time fields.
Add vectorcall fast paths for dict, list, set, int, float, str, bool, tuple, frozenset. Clear vectorcall when __init__/__new__ is overridden in Python. Prevent constructor vectorcall inheritance to heap subclasses. Fix stat_result to use struct_sequence_new with reference-copy for hidden time fields.
Add vectorcall fast paths for dict, list, set, int, float, str, bool, tuple, frozenset. Clear vectorcall when __init__/__new__ is overridden in Python. Prevent constructor vectorcall inheritance to heap subclasses. Fix stat_result to use struct_sequence_new with reference-copy for hidden time fields.
close #7362
Add vectorcall dispatch in vectorcall_type to check each type's own slots.vectorcall before falling back to PyType::call. Clear vectorcall when init or new is overridden in update_slot to ensure subclass correctness.
Implement per-type vectorcall for 12 builtin types:
Summary by CodeRabbit
sys.getwindowsversion, now raises TypeError.