Skip to content

Conversation

@youknowone
Copy link
Member

@youknowone youknowone commented Jan 24, 2026

#6839

Summary by CodeRabbit

  • New Features

    • Added several msvcrt functions (ungetch, ungetwch, kbhit, locking, heapmin) and related locking mode constants.
    • Exposed many additional Windows file-operation constants and a CreateFile wrapper for improved file access.
    • Enhanced test tooling to support multiple test paths, better extension-module test resolution, and more robust error handling.
  • Chores

    • Ensure UTF-8 encoding when reading/writing files in library migration scripts.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 24, 2026

📝 Walkthrough

Walkthrough

These changes extend the Windows API bindings for RustPython by adding msvcrt functions (ungetch, ungetwch, kbhit, locking, heapmin), exposing FileSystem constants, and implementing a CreateFile wrapper. Update scripts are enhanced with explicit UTF-8 encoding and improved extension module test resolution.

Changes

Cohort / File(s) Summary
msvcrt bindings
crates/vm/src/stdlib/msvcrt.rs
Added FFI declarations and Python bindings for _ungetch, _ungetwch, _kbhit, _locking, _heapmin; added locking constants LK_UNLCK, LK_LOCK, LK_NBLCK, LK_RLCK, LK_NBRLCK; includes argument validation and errno-based error mapping.
_winapi FileSystem & CreateFile
crates/vm/src/stdlib/winapi.rs
Exposed many Windows FileSystem constants (COPY_FILE_, FILE_ATTRIBUTE_, FILE_FLAG_, FILE_SHARE_, FILE_TYPE_, OPEN_, PIPE_ACCESS_*, SYNCHRONIZE, TRUNCATE_EXISTING, etc.) and added CreateFile wrapper calling CreateFileW with wide-string conversion and NULL handling; returns WinHandle or OS error.
update_lib: migrate I/O encoding
scripts/update_lib/migrate.py
Added explicit encoding="utf-8" to all Path.read_text/write_text calls to ensure deterministic file I/O.
update_lib: multi-path tests & shortcuts
scripts/update_lib/quick.py
Added multi-test-path support (normalize test_paths), improved _expand_shortcut to handle extension-module test fallback (test/test_<name>), per-path processing in main, and user-friendly error handling; updated git_commit signature to accept multiple test paths.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Possibly related PRs

Suggested labels

skip:ci

Suggested reviewers

  • ShaharNaveh

Poem

🐰
I hopped through bytes and constants bright,
Pushed CreateFile into the light,
Ungetch and heap trimmed tidy too,
UTF‑8 paths—clean and true,
A rabbit's cheer for code anew! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Title check ⚠️ Warning The title mentions 'urllib and windows libraries' but the changeset only updates Windows-related msvcrt and winapi bindings, with a minor encoding fix in migration scripts; urllib is not present in the changes. Change the title to accurately reflect the actual changes: something like 'Add msvcrt and winapi Windows library bindings' or 'Extend Windows API bindings for msvcrt and winapi'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 24, 2026

Code has been automatically formatted

The code in this PR has been formatted using:

  • ruff check --select I --fix
    Please pull the latest changes before pushing again:
git pull origin win-lib

@youknowone youknowone marked this pull request as ready for review January 24, 2026 03:54
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@crates/vm/src/stdlib/msvcrt.rs`:
- Line 36: The FFI declaration fn _locking and the public wrapper locking() use
i64 for the nbytes parameter which mismatches the Windows CRT ABI (should be
32-bit long); change the third parameter type from i64 to i32 in the extern "C"
fn _locking declaration and in the pub fn locking(...) wrapper signature and any
internal uses/casts in that wrapper to pass an i32. Ensure you do not try to
reference a non-existent _locking64 export; if larger ranges are needed, use
Win32 LockFile/LockFileEx instead.
🧹 Nitpick comments (1)
crates/vm/src/stdlib/msvcrt.rs (1)

122-125: Consider returning bool for kbhit() to match CPython.

CPython's msvcrt.kbhit() returns a boolean (True/False), while this implementation returns the raw i32. The current implementation works but doesn't strictly match Python semantics.

♻️ Optional: Return bool for Python compatibility
 #[pyfunction]
-fn kbhit() -> i32 {
-    unsafe { _kbhit() }
+fn kbhit() -> bool {
+    unsafe { _kbhit() != 0 }
 }

fn _putwch(c: u16) -> u32;
fn _ungetch(c: i32) -> i32;
fn _ungetwch(c: u32) -> u32;
fn _locking(fd: i32, mode: i32, nbytes: i64) -> i32;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cd crates/vm/src/stdlib && wc -l msvcrt.rs

Repository: RustPython/RustPython

Length of output: 78


🏁 Script executed:

cd crates/vm/src/stdlib && sed -n '30,45p' msvcrt.rs

Repository: RustPython/RustPython

Length of output: 535


🏁 Script executed:

cd crates/vm/src/stdlib && sed -n '120,140p' msvcrt.rs

Repository: RustPython/RustPython

Length of output: 595


🌐 Web query:

Windows CRT _locking function signature nbytes parameter type int long

💡 Result:

In the Microsoft C Runtime (CRT), _locking has this signature:

int _locking(int fd, int mode, long nbytes);

So the nbytes parameter type is long (signed long), not int. [1]

Source: Microsoft Learn _locking documentation. [1]

[1] (learn.microsoft.com)

Citations:


🌐 Web query:

_locking vs _locking64 Windows C runtime function difference

💡 Result:

  • _locking is the (documented) MSVC C runtime function that locks/unlocks a byte range starting at the current file pointer for nbytes bytes, where nbytes is a 32-bit long:
    int _locking(int fd, int mode, long nbytes); This means the length you can request in a single call is limited by 32-bit long range (and long stays 32-bit even on 64-bit Windows). [1]

  • _locking64 is not a standard/documented MSVC CRT API (at least not in the classic msvcrt.dll export surface): msvcrt.dll exports _locking but does not list an _locking64 export. [2] A separate MSVCRT export-definition listing likewise shows _locking but not _locking64. [3]

What to use instead (for “64-bit”/large-range locks): use the Win32 file-locking APIs (LockFile / LockFileEx), which support 64-bit ranges via low/high parts. [4]

Sources:
[1] Microsoft Learn: _locking signature/behavior https://learn.microsoft.com/en-us/previous-versions/8054ew2f(v=vs.140)
[2] msvcrt.dll exported functions list (shows _locking, no _locking64) https://strontic.github.io/xcyclopedia/library/msvcrt.dll-6297CA08C873060692FF97074895FA95.html
[3] MSVCRT export .def listing (shows _locking, no _locking64) https://www-user.tu-chemnitz.de/~heha/viewzip.php/hs/msvcrt-light.zip/src/msvcrt.winxp-x64.def?as=DEF
[4] Microsoft Learn: byte-range locking overview (LockFile / LockFileEx) https://learn.microsoft.com/en-us/windows/win32/fileio/locking-and-unlocking-byte-ranges-in-files


Change nbytes parameter from i64 to i32 to match Windows CRT ABI.

The Windows CRT _locking function signature specifies long (32-bit on Windows, even on 64-bit systems) for the nbytes parameter:

int _locking(int fd, int mode, long nbytes);

Using i64 at line 36 and in the wrapper function (lines 127-135) causes an ABI mismatch that can lead to undefined behavior. Change to i32 for both the FFI declaration and the public locking() function signature. Note: _locking64 is not a standard export in msvcrt.dll; if 64-bit file range locking is needed, use Win32 APIs like LockFile or LockFileEx.

🤖 Prompt for AI Agents
In `@crates/vm/src/stdlib/msvcrt.rs` at line 36, The FFI declaration fn _locking
and the public wrapper locking() use i64 for the nbytes parameter which
mismatches the Windows CRT ABI (should be 32-bit long); change the third
parameter type from i64 to i32 in the extern "C" fn _locking declaration and in
the pub fn locking(...) wrapper signature and any internal uses/casts in that
wrapper to pass an i32. Ensure you do not try to reference a non-existent
_locking64 export; if larger ranges are needed, use Win32 LockFile/LockFileEx
instead.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@crates/vm/src/stdlib/winapi.rs`:
- Around line 119-153: The CreateFile wrapper currently ignores the passed-in
_security_attributes and _template_file but still accepts non-None values;
update CreateFile to validate these parameters (the _security_attributes and
_template_file PyObjectRef args) and reject any non-None input by returning a
Python error via the VirtualMachine (e.g., Err(vm.new_type_error(...)) or
vm.new_value_error(...)) with a clear message like "security_attributes must be
None" / "template_file must be None"; perform these checks before calling
CreateFileW so callers are explicitly informed the function does not support
non-None security/template arguments.
♻️ Duplicate comments (1)
crates/vm/src/stdlib/msvcrt.rs (1)

34-38: ABI mismatch: _locking expects 32‑bit long, not i64.

Using i64 risks UB in FFI calls. This matches the earlier review finding; please adjust both the extern signature and wrapper parameter to i32.

🛠️ Proposed fix
-        fn _locking(fd: i32, mode: i32, nbytes: i64) -> i32;
+        fn _locking(fd: i32, mode: i32, nbytes: i32) -> i32;
@@
-    fn locking(fd: i32, mode: i32, nbytes: i64, vm: &VirtualMachine) -> PyResult<()> {
+    fn locking(fd: i32, mode: i32, nbytes: i32, vm: &VirtualMachine) -> PyResult<()> {
MSVC _locking function signature and nbytes parameter type (long vs long long)

Also applies to: 127-135

Comment on lines +119 to +153
/// CreateFile - Create or open a file or I/O device.
#[pyfunction]
#[allow(clippy::too_many_arguments)]
fn CreateFile(
file_name: PyStrRef,
desired_access: u32,
share_mode: u32,
_security_attributes: PyObjectRef, // Always NULL (0)
creation_disposition: u32,
flags_and_attributes: u32,
_template_file: PyObjectRef, // Always NULL (0)
vm: &VirtualMachine,
) -> PyResult<WinHandle> {
use windows_sys::Win32::Storage::FileSystem::CreateFileW;

let file_name_wide = file_name.as_wtf8().to_wide_with_nul();

let handle = unsafe {
CreateFileW(
file_name_wide.as_ptr(),
desired_access,
share_mode,
null(),
creation_disposition,
flags_and_attributes,
null_mut(),
)
};

if handle == INVALID_HANDLE_VALUE {
return Err(vm.new_last_os_error());
}

Ok(WinHandle(handle))
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

CPython _winapi CreateFile security_attributes template_file implementation

💡 Result:

In CPython, _winapi.CreateFile is a very thin wrapper over the Win32 CreateFile API:

  • Signature in Modules/_winapi.c (shown here from CPython v3.9.7) is:

    _winapi.CreateFile(file_name, desired_access, share_mode,
                      security_attributes, creation_disposition,
                      flags_and_attributes, template_file) -> HANDLE
    

    and the implementation just calls CreateFile(file_name, ..., security_attributes, ..., template_file) (wrapped in Py_BEGIN_ALLOW_THREADS) and raises OSError on INVALID_HANDLE_VALUE. (chromium.googlesource.com)

  • security_attributes is declared as LPSECURITY_ATTRIBUTES, but Argument Clinic defines LPSECURITY_ATTRIBUTES as a raw pointer-sized integer (“k” on 32-bit, “K” on 64-bit) (i.e., an address), not a Python object representing a SECURITY_ATTRIBUTES struct. (chromium.googlesource.com)
    Practically: pass 0 to mean NULL (no security attrs / non-inheritable handle by default), or pass an integer address to a valid SECURITY_ATTRIBUTES struct you created elsewhere in native code.

  • template_file is a HANDLE (also passed straight through). Pass 0/NULL for “no template file”. (chromium.googlesource.com)

For the Win32 meaning of these two parameters:

  • lpSecurityAttributes can be NULL; it controls the optional security descriptor and whether the returned handle is inheritable. (learn.microsoft.com)
  • hTemplateFile can be NULL; it supplies attributes/extended attributes for a newly created file and is ignored when opening an existing file. (learn.microsoft.com)

Citations:


🌐 Web query:

Windows API CreateFileW security_attributes template_file parameter significance

💡 Result:

lpSecurityAttributes (4th parameter)

Controls (a) the new object’s security and (b) whether the returned handle is inheritable:

  • Points to a SECURITY_ATTRIBUTES containing:
    • lpSecurityDescriptor: security descriptor used only when a new file/object is created; ignored when opening an existing file, but…
    • bInheritHandle: still controls whether the returned handle can be inherited by child processes. (learn.microsoft.com)
  • If lpSecurityAttributes == NULL:
    • the handle is not inheritable
    • a newly created file gets a default security descriptor (ACLs typically inherited from the parent directory). (learn.microsoft.com)

hTemplateFile (7th parameter)

Provides attributes/extended attributes to copy when creating a new file:

  • A handle to an existing template file (must be open with GENERIC_READ) whose file attributes and extended attributes are applied to the new file. (learn.microsoft.com)
  • Ignored when opening an existing file. (learn.microsoft.com)
  • When creating, if a template is supplied, its extended attributes are copied and can override FILE_ATTRIBUTE_* choices you passed in dwFlagsAndAttributes. (learn.microsoft.com)

Citations:


🏁 Script executed:

# Check if there are any tests or other code in the repository that might use CreateFile with these parameters
fd -t f "\.rs$" | xargs rg -l "CreateFile" | head -20

Repository: RustPython/RustPython

Length of output: 147


🏁 Script executed:

# Check if there are any test files or usage examples for CreateFile
rg -t rs "CreateFile" crates/vm/src --context 5

# Also check if there are tests specifically for this function
fd -t f "test.*\.rs|.*_test\.rs" | xargs rg -l "CreateFile" 2>/dev/null || echo "No test files found with CreateFile"

Repository: RustPython/RustPython

Length of output: 144


🏁 Script executed:

# Check the implementation context to understand how other similar parameters are handled
cat -n crates/vm/src/stdlib/winapi.rs | head -200

Repository: RustPython/RustPython

Length of output: 8794


Add guard to reject non-None security_attributes and template_file parameters.

The function signature accepts these as PyObjectRef, but silently ignores non-None values and always passes NULL to the Win32 API. This can mislead callers who pass non-None values expecting ACL, handle inheritance, or template attribute behavior. If support isn't ready, explicitly reject non-None values to make the limitation clear.

Suggested implementation
 fn CreateFile(
     file_name: PyStrRef,
     desired_access: u32,
     share_mode: u32,
     _security_attributes: PyObjectRef,
     creation_disposition: u32,
     flags_and_attributes: u32,
     _template_file: PyObjectRef,
     vm: &VirtualMachine,
 ) -> PyResult<WinHandle> {
     use windows_sys::Win32::Storage::FileSystem::CreateFileW;

+    if !_security_attributes.is(&vm.ctx.none()) || !_template_file.is(&vm.ctx.none()) {
+        return Err(vm.new_not_implemented_error(
+            "security_attributes and template_file are not supported yet".to_owned(),
+        ));
+    }
+
     let file_name_wide = file_name.as_wtf8().to_wide_with_nul();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// CreateFile - Create or open a file or I/O device.
#[pyfunction]
#[allow(clippy::too_many_arguments)]
fn CreateFile(
file_name: PyStrRef,
desired_access: u32,
share_mode: u32,
_security_attributes: PyObjectRef, // Always NULL (0)
creation_disposition: u32,
flags_and_attributes: u32,
_template_file: PyObjectRef, // Always NULL (0)
vm: &VirtualMachine,
) -> PyResult<WinHandle> {
use windows_sys::Win32::Storage::FileSystem::CreateFileW;
let file_name_wide = file_name.as_wtf8().to_wide_with_nul();
let handle = unsafe {
CreateFileW(
file_name_wide.as_ptr(),
desired_access,
share_mode,
null(),
creation_disposition,
flags_and_attributes,
null_mut(),
)
};
if handle == INVALID_HANDLE_VALUE {
return Err(vm.new_last_os_error());
}
Ok(WinHandle(handle))
}
/// CreateFile - Create or open a file or I/O device.
#[pyfunction]
#[allow(clippy::too_many_arguments)]
fn CreateFile(
file_name: PyStrRef,
desired_access: u32,
share_mode: u32,
_security_attributes: PyObjectRef, // Always NULL (0)
creation_disposition: u32,
flags_and_attributes: u32,
_template_file: PyObjectRef, // Always NULL (0)
vm: &VirtualMachine,
) -> PyResult<WinHandle> {
use windows_sys::Win32::Storage::FileSystem::CreateFileW;
if !_security_attributes.is(&vm.ctx.none()) || !_template_file.is(&vm.ctx.none()) {
return Err(vm.new_not_implemented_error(
"security_attributes and template_file are not supported yet".to_owned(),
));
}
let file_name_wide = file_name.as_wtf8().to_wide_with_nul();
let handle = unsafe {
CreateFileW(
file_name_wide.as_ptr(),
desired_access,
share_mode,
null(),
creation_disposition,
flags_and_attributes,
null_mut(),
)
};
if handle == INVALID_HANDLE_VALUE {
return Err(vm.new_last_os_error());
}
Ok(WinHandle(handle))
}
🤖 Prompt for AI Agents
In `@crates/vm/src/stdlib/winapi.rs` around lines 119 - 153, The CreateFile
wrapper currently ignores the passed-in _security_attributes and _template_file
but still accepts non-None values; update CreateFile to validate these
parameters (the _security_attributes and _template_file PyObjectRef args) and
reject any non-None input by returning a Python error via the VirtualMachine
(e.g., Err(vm.new_type_error(...)) or vm.new_value_error(...)) with a clear
message like "security_attributes must be None" / "template_file must be None";
perform these checks before calling CreateFileW so callers are explicitly
informed the function does not support non-None security/template arguments.

@youknowone youknowone changed the title Upgrade windows libraries Update urllib and windows libraries from v3.14.2 Jan 24, 2026
@github-actions
Copy link
Contributor

📦 Library Dependencies

The following Lib/ modules were modified. Here are their dependencies:

[+] lib: cpython/Lib/ntpath.py
[+] test: cpython/Lib/test/test_ntpath.py

dependencies:

  • ntpath

dependent tests: (3 tests)

  • ntpath: test_httpservers test_ntpath test_pathlib

[+] lib: cpython/Lib/nturl2path.py
[+] test: cpython/Lib/test/test_nturl2path.py

dependencies:

  • nturl2path

dependent tests: (no tests depend on nturl2path)

[+] lib: cpython/Lib/genericpath.py
[+] test: cpython/Lib/test/test_genericpath.py

dependencies:

  • genericpath

dependent tests: (16 tests)

  • genericpath: test_genericpath
    • ntpath: test_httpservers test_ntpath test_pathlib
    • posixpath: test_pathlib test_posixpath test_zipfile
      • fnmatch: test_fnmatch test_os
      • http.server: test_logging test_robotparser test_urllib2_localnet
      • importlib.metadata: test_importlib test_zoneinfo
      • wsgiref.util: test_wsgiref
      • zipfile._path: test_zipfile

[+] test: cpython/Lib/test/test_msvcrt.py

dependencies:

dependent tests: (1 tests)

  • msvcrt: test_msvcrt

[+] test: cpython/Lib/test/test_robotparser.py

dependencies:

dependent tests: (no tests depend on robotparser)

[+] lib: cpython/Lib/urllib
[+] test: cpython/Lib/test/test_urllib.py
[+] test: cpython/Lib/test/test_urllib2.py
[+] test: cpython/Lib/test/test_urllib2_localnet.py
[+] test: cpython/Lib/test/test_urllib2net.py
[+] test: cpython/Lib/test/test_urllibnet.py
[+] test: cpython/Lib/test/test_urlparse.py
[+] test: cpython/Lib/test/test_urllib_response.py
[+] test: cpython/Lib/test/test_robotparser.py

dependencies:

  • urllib

dependent tests: (26 tests)

  • urllib: test_genericalias test_http_cookiejar test_httpservers test_logging test_pathlib test_robotparser test_site test_sqlite3 test_ssl test_ucn test_urllib test_urllib2 test_urllib2_localnet test_urllib2net test_urllib_response test_urllibnet test_urlparse
    • email.utils: test_email test_smtplib
      • smtplib: test_smtpnet
    • http.client: test_docxmlrpc test_hashlib test_unicodedata test_wsgiref test_xmlrpc
    • pydoc: test_enum

[+] test: cpython/Lib/test/test_urllib2.py

dependencies:

dependent tests: (no tests depend on urllib2)

[+] test: cpython/Lib/test/test_urllib2_localnet.py

dependencies:

dependent tests: (no tests depend on urllib2_localnet)

[+] test: cpython/Lib/test/test_urllib2net.py

dependencies:

dependent tests: (no tests depend on urllib2net)

[+] test: cpython/Lib/test/test_urllibnet.py

dependencies:

dependent tests: (no tests depend on urllibnet)

[+] test: cpython/Lib/test/test_urlparse.py

dependencies:

dependent tests: (no tests depend on urlparse)

[+] test: cpython/Lib/test/test_winapi.py

dependencies:

dependent tests: (no tests depend on winapi)

Legend:

  • [+] path exists in CPython
  • [x] up-to-date, [ ] outdated

@youknowone youknowone merged commit 4963cc6 into RustPython:main Jan 24, 2026
14 checks passed
@youknowone youknowone deleted the win-lib branch January 24, 2026 15:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant