Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions Lib/test/test_selectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,6 @@ def test_unregister_after_fd_close_and_reuse(self):
s.unregister(r)
s.unregister(w)

# TODO: RUSTPYTHON
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
def test_unregister_after_socket_close(self):
s = self.SELECTOR()
self.addCleanup(s.close)
Expand Down
5 changes: 0 additions & 5 deletions Lib/test/test_socketserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,13 +178,11 @@ def dgram_examine(self, proto, addr):
buf += data
self.assertEqual(buf, TEST_STR)

@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; AssertionError: -1 != 18446744073709551615")
def test_TCPServer(self):
self.run_server(socketserver.TCPServer,
socketserver.StreamRequestHandler,
self.stream_examine)

@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; AssertionError: -1 != 18446744073709551615")
def test_ThreadingTCPServer(self):
self.run_server(socketserver.ThreadingTCPServer,
socketserver.StreamRequestHandler,
Expand Down Expand Up @@ -217,13 +215,11 @@ def test_ForkingUnixStreamServer(self):
socketserver.StreamRequestHandler,
self.stream_examine)

@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; AssertionError: -1 != 18446744073709551615")
def test_UDPServer(self):
self.run_server(socketserver.UDPServer,
socketserver.DatagramRequestHandler,
self.dgram_examine)

@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; AssertionError: -1 != 18446744073709551615")
def test_ThreadingUDPServer(self):
self.run_server(socketserver.ThreadingUDPServer,
socketserver.DatagramRequestHandler,
Expand Down Expand Up @@ -298,7 +294,6 @@ def test_tcpserver_bind_leak(self):
socketserver.TCPServer((HOST, -1),
socketserver.StreamRequestHandler)

@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON; AssertionError: -1 != 18446744073709551615")
def test_context_manager(self):
with socketserver.TCPServer((HOST, 0),
socketserver.StreamRequestHandler) as server:
Expand Down
1 change: 0 additions & 1 deletion Lib/test/test_zipimport.py
Original file line number Diff line number Diff line change
Expand Up @@ -730,7 +730,6 @@ def testTraceback(self):

@unittest.skipIf(os_helper.TESTFN_UNENCODABLE is None,
"need an unencodable filename")
@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
def testUnencodable(self):
filename = os_helper.TESTFN_UNENCODABLE + ".zip"
self.addCleanup(os_helper.unlink, filename)
Expand Down
15 changes: 7 additions & 8 deletions crates/stdlib/src/socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1065,8 +1065,7 @@ mod _socket {
fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
Ok(format!(
"<socket object, fd={}, family={}, type={}, proto={}>",
// cast because INVALID_SOCKET is unsigned, so would show usize::MAX instead of -1
zelf.fileno() as i64,
zelf.fileno(),
zelf.family.load(),
zelf.kind.load(),
zelf.proto.load(),
Expand Down Expand Up @@ -1462,25 +1461,25 @@ mod _socket {
#[pymethod]
fn close(&self) -> io::Result<()> {
let sock = self.detach();
if sock != INVALID_SOCKET {
close_inner(sock)?;
if sock != INVALID_SOCKET as i64 {
close_inner(sock as RawSocket)?;
}
Ok(())
}

#[pymethod]
#[inline]
fn detach(&self) -> RawSocket {
fn detach(&self) -> i64 {
let sock = self.sock.write().take();
sock.map_or(INVALID_SOCKET, into_sock_fileno)
sock.map_or(INVALID_SOCKET as i64, |s| into_sock_fileno(s) as i64)
}
Comment on lines +1472 to 1475
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 | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

rg -n --type rust '\.detach\s*\(' -B 3 -A 3

Repository: RustPython/RustPython

Length of output: 508


🏁 Script executed:

rg -n 'fn detach' crates/stdlib/src/socket.rs -B 2 -A 5

Repository: RustPython/RustPython

Length of output: 335


🏁 Script executed:

rg -n 'into_sock_fileno|close_inner' crates/stdlib/src/socket.rs -B 2 -A 5

Repository: RustPython/RustPython

Length of output: 1943


🏁 Script executed:

rg -n 'fn fileno' crates/stdlib/src/socket.rs -B 2 -A 5

Repository: RustPython/RustPython

Length of output: 315


🏁 Script executed:

rg -n 'INVALID_SOCKET|cfg.*windows' crates/stdlib/src/socket.rs | head -40

Repository: RustPython/RustPython

Length of output: 1563


🏁 Script executed:

sed -n '2374,2385p' crates/stdlib/src/socket.rs

Repository: RustPython/RustPython

Length of output: 333


🏁 Script executed:

rg -n 'RawSocket' crates/stdlib/src/socket.rs | grep -E 'type|cfg' | head -20

Repository: RustPython/RustPython

Length of output: 171


🏁 Script executed:

# Check if there are any comments or TODOs about Windows socket limitations
rg -n 'TODO|FIXME|BUG|XXX|HACK' crates/stdlib/src/socket.rs | grep -i 'socket\|window\|handle\|fd'

Repository: RustPython/RustPython

Length of output: 47


Verify the return type change impact across the codebase.

The detach() method now returns i64 instead of RawSocket. This is a breaking API change affecting:

  1. The close() method (line 1463), which has been properly updated to handle i64 and cast back to RawSocket for close_inner()
  2. Python code calling detach(), which receives i64 (Python's int safely represents this range)

On Windows, casting RawSocket (which is usize) to i64 can theoretically overflow if a socket handle exceeds i64::MAX. This same issue also affects the fileno() method (line 1482), suggesting it may be an accepted limitation of the current design. Consider documenting whether this is a known limitation or requires a fix.


#[pymethod]
fn fileno(&self) -> RawSocket {
fn fileno(&self) -> i64 {
self.sock
.read()
.as_ref()
.map_or(INVALID_SOCKET, sock_fileno)
.map_or(INVALID_SOCKET as i64, |s| sock_fileno(s) as i64)
}

#[pymethod]
Expand Down
28 changes: 17 additions & 11 deletions crates/vm/src/stdlib/nt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1122,10 +1122,18 @@ pub(crate) mod module {
}

#[pyfunction]
fn _path_splitroot(path: OsPath, vm: &VirtualMachine) -> PyResult<(String, String)> {
fn _path_splitroot(
path: OsPath,
_vm: &VirtualMachine,
) -> (
rustpython_common::wtf8::Wtf8Buf,
rustpython_common::wtf8::Wtf8Buf,
) {
use rustpython_common::wtf8::Wtf8Buf;

let orig: Vec<_> = path.path.to_wide();
if orig.is_empty() {
return Ok(("".to_owned(), "".to_owned()));
return (Wtf8Buf::new(), Wtf8Buf::new());
}
let backslashed: Vec<_> = orig
.iter()
Expand All @@ -1134,15 +1142,11 @@ pub(crate) mod module {
.chain(std::iter::once(0)) // null-terminated
.collect();

fn from_utf16(wstr: &[u16], vm: &VirtualMachine) -> PyResult<String> {
String::from_utf16(wstr).map_err(|e| vm.new_unicode_decode_error(e.to_string()))
}

let mut end: *const u16 = std::ptr::null();
let hr = unsafe {
windows_sys::Win32::UI::Shell::PathCchSkipRoot(backslashed.as_ptr(), &mut end)
};
let (root, path) = if hr == 0 {
if hr == 0 {
// S_OK
assert!(!end.is_null());
let len: usize = unsafe { end.offset_from(backslashed.as_ptr()) }
Expand All @@ -1155,11 +1159,13 @@ pub(crate) mod module {
len,
backslashed.len()
);
(from_utf16(&orig[..len], vm)?, from_utf16(&orig[len..], vm)?)
(
Wtf8Buf::from_wide(&orig[..len]),
Wtf8Buf::from_wide(&orig[len..]),
)
} else {
("".to_owned(), from_utf16(&orig, vm)?)
};
Ok((root, path))
(Wtf8Buf::new(), Wtf8Buf::from_wide(&orig))
}
}

#[pyfunction]
Expand Down
Loading