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
4 changes: 0 additions & 4 deletions Lib/test/test_regrtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -820,8 +820,6 @@ def test_fromfile(self):
output = self.run_tests('--fromfile', filename)
self.check_executed_tests(output, tests)

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_interrupted(self):
code = TEST_INTERRUPTED
test = self.create_test('sigint', code=code)
Expand All @@ -839,8 +837,6 @@ def test_slowest(self):
% (self.TESTNAME_REGEX, len(tests)))
self.check_line(output, regex)

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_slowest_interrupted(self):
# Issue #25373: test --slowest with an interrupted test
code = TEST_INTERRUPTED
Expand Down
16 changes: 0 additions & 16 deletions Lib/test/test_signal.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,6 @@ def trivial_signal_handler(self, *args):
def create_handler_with_partial(self, argument):
return functools.partial(self.trivial_signal_handler, argument)

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_out_of_range_signal_number_raises_error(self):
self.assertRaises(ValueError, signal.getsignal, 4242)

Expand Down Expand Up @@ -126,8 +124,6 @@ def __repr__(self):
self.assertEqual(signal.getsignal(signal.SIGHUP), hup)
self.assertEqual(0, argument.repr_count)

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_strsignal(self):
self.assertIn("Interrupt", signal.strsignal(signal.SIGINT))
self.assertIn("Terminated", signal.strsignal(signal.SIGTERM))
Expand All @@ -141,8 +137,6 @@ def test_interprocess_signal(self):
script = os.path.join(dirname, 'signalinterproctester.py')
assert_python_ok(script)

# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.skipUnless(
hasattr(signal, "valid_signals"),
"requires signal.valid_signals"
Expand Down Expand Up @@ -190,8 +184,6 @@ def test_keyboard_interrupt_exit_code(self):
@unittest.skipUnless(sys.platform == "win32", "Windows specific")
class WindowsSignalTests(unittest.TestCase):

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_valid_signals(self):
s = signal.valid_signals()
self.assertIsInstance(s, set)
Expand Down Expand Up @@ -251,13 +243,11 @@ def test_invalid_call(self):
with self.assertRaises(TypeError):
signal.set_wakeup_fd(signal.SIGINT, False)

@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
def test_invalid_fd(self):
fd = os_helper.make_bad_fd()
self.assertRaises((ValueError, OSError),
signal.set_wakeup_fd, fd)

@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
@unittest.skipUnless(support.has_socket_support, "needs working sockets.")
def test_invalid_socket(self):
sock = socket.socket()
Expand All @@ -266,7 +256,6 @@ def test_invalid_socket(self):
self.assertRaises((ValueError, OSError),
signal.set_wakeup_fd, fd)

@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
# Emscripten does not support fstat on pipes yet.
# https://github.com/emscripten-core/emscripten/issues/16414
@unittest.skipIf(support.is_emscripten, "Emscripten cannot fstat pipes.")
Expand All @@ -288,7 +277,6 @@ def test_set_wakeup_fd_result(self):
self.assertEqual(signal.set_wakeup_fd(-1), w2)
self.assertEqual(signal.set_wakeup_fd(-1), -1)

@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
@unittest.skipIf(support.is_emscripten, "Emscripten cannot fstat pipes.")
@unittest.skipUnless(support.has_socket_support, "needs working sockets.")
def test_set_wakeup_fd_socket_result(self):
Expand Down Expand Up @@ -1440,8 +1428,6 @@ def cycle_handlers():

class RaiseSignalTest(unittest.TestCase):

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_sigint(self):
with self.assertRaises(KeyboardInterrupt):
signal.raise_signal(signal.SIGINT)
Expand All @@ -1460,8 +1446,6 @@ def test_invalid_argument(self):
else:
raise

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_handler(self):
is_ok = False
def handler(a, b):
Expand Down
1 change: 0 additions & 1 deletion Lib/test/test_strftime.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,6 @@ class Y1900Tests(unittest.TestCase):
a date before 1900 is passed with a format string containing "%y"
"""

@unittest.expectedFailureIfWindows("TODO: RUSTPYTHON")
def test_y_before_1900(self):
# Issue #13674, #19634
t = (1899, 1, 1, 0, 0, 0, 0, 0, 0)
Expand Down
134 changes: 132 additions & 2 deletions crates/vm/src/stdlib/signal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ pub(crate) mod _signal {
}

#[pyfunction]
fn set_wakeup_fd(args: SetWakeupFdArgs, vm: &VirtualMachine) -> PyResult<WakeupFdRaw> {
fn set_wakeup_fd(args: SetWakeupFdArgs, vm: &VirtualMachine) -> PyResult<i64> {
// TODO: implement warn_on_full_buffer
let _ = args.warn_on_full_buffer;
#[cfg(windows)]
Expand Down Expand Up @@ -264,6 +264,15 @@ pub(crate) mod _signal {
if err.raw_os_error() != Some(WinSock::WSAENOTSOCK) {
return Err(err.into_pyexception(vm));
}
// Validate that fd is a valid file descriptor using fstat
// First check if SOCKET can be safely cast to i32 (file descriptor)
let fd_i32 =
i32::try_from(fd).map_err(|_| vm.new_value_error("invalid fd".to_owned()))?;
// Verify the fd is valid by trying to fstat it
let borrowed_fd =
unsafe { crate::common::crt_fd::Borrowed::try_borrow_raw(fd_i32) }
.map_err(|e| e.into_pyexception(vm))?;
crate::common::fileutils::fstat(borrowed_fd).map_err(|e| e.into_pyexception(vm))?;
}
is_socket
} else {
Expand All @@ -287,7 +296,18 @@ pub(crate) mod _signal {
#[cfg(windows)]
WAKEUP_IS_SOCKET.store(is_socket, Ordering::Relaxed);

Ok(old_fd)
#[cfg(windows)]
{
if old_fd == INVALID_WAKEUP {
Ok(-1)
} else {
Ok(old_fd as i64)
}
}
#[cfg(not(windows))]
{
Ok(old_fd as i64)
}
}

#[cfg(all(unix, not(target_os = "redox")))]
Expand All @@ -302,6 +322,116 @@ pub(crate) mod _signal {
}
}

/// CPython: signal_raise_signal (signalmodule.c)
#[cfg(any(unix, windows))]
#[pyfunction]
fn raise_signal(signalnum: i32, vm: &VirtualMachine) -> PyResult<()> {
signal::assert_in_range(signalnum, vm)?;

// On Windows, only certain signals are supported
#[cfg(windows)]
{
use crate::convert::IntoPyException;
// Windows supports: SIGINT(2), SIGILL(4), SIGFPE(8), SIGSEGV(11), SIGTERM(15), SIGABRT(22)
const VALID_SIGNALS: &[i32] = &[
libc::SIGINT,
libc::SIGILL,
libc::SIGFPE,
libc::SIGSEGV,
libc::SIGTERM,
libc::SIGABRT,
];
if !VALID_SIGNALS.contains(&signalnum) {
return Err(std::io::Error::from_raw_os_error(libc::EINVAL).into_pyexception(vm));
}
}

let res = unsafe { libc::raise(signalnum) };
if res != 0 {
return Err(vm.new_os_error(format!("raise_signal failed for signal {}", signalnum)));
}

// Check if a signal was triggered and handle it
signal::check_signals(vm)?;

Ok(())
}

/// CPython: signal_strsignal (signalmodule.c)
#[cfg(unix)]
#[pyfunction]
fn strsignal(signalnum: i32, vm: &VirtualMachine) -> PyResult<Option<String>> {
if signalnum < 1 || signalnum >= signal::NSIG as i32 {
return Err(vm.new_value_error(format!("signal number {} out of range", signalnum)));
}
let s = unsafe { libc::strsignal(signalnum) };
if s.is_null() {
Ok(None)
} else {
let cstr = unsafe { std::ffi::CStr::from_ptr(s) };
Ok(Some(cstr.to_string_lossy().into_owned()))
}
}

#[cfg(windows)]
#[pyfunction]
fn strsignal(signalnum: i32, vm: &VirtualMachine) -> PyResult<Option<String>> {
if signalnum < 1 || signalnum >= signal::NSIG as i32 {
return Err(vm.new_value_error(format!("signal number {} out of range", signalnum)));
}
// Windows doesn't have strsignal(), provide our own mapping
let name = match signalnum {
libc::SIGINT => "Interrupt",
libc::SIGILL => "Illegal instruction",
libc::SIGFPE => "Floating-point exception",
libc::SIGSEGV => "Segmentation fault",
libc::SIGTERM => "Terminated",
libc::SIGABRT => "Aborted",
_ => return Ok(None),
};
Ok(Some(name.to_owned()))
}

/// CPython: signal_valid_signals (signalmodule.c)
#[pyfunction]
fn valid_signals(vm: &VirtualMachine) -> PyResult {
use crate::PyPayload;
use crate::builtins::PySet;
let set = PySet::default().into_ref(&vm.ctx);
#[cfg(unix)]
{
// On Unix, most signals 1..NSIG are valid
for signum in 1..signal::NSIG {
// Skip signals that cannot be caught
#[cfg(not(target_os = "wasi"))]
if signum == libc::SIGKILL as usize || signum == libc::SIGSTOP as usize {
continue;
}
set.add(vm.ctx.new_int(signum as i32).into(), vm)?;
}
}
#[cfg(windows)]
{
// Windows only supports a limited set of signals
for &signum in &[
libc::SIGINT,
libc::SIGILL,
libc::SIGFPE,
libc::SIGSEGV,
libc::SIGTERM,
libc::SIGABRT,
] {
set.add(vm.ctx.new_int(signum).into(), vm)?;
}
}
#[cfg(not(any(unix, windows)))]
{
// Empty set for platforms without signal support (e.g., WASM)
let _ = &set;
}
Ok(set.into())
}

#[cfg(any(unix, windows))]
pub extern "C" fn run_signal(signum: i32) {
signal::TRIGGERS[signum as usize].store(true, Ordering::Relaxed);
Expand Down
8 changes: 8 additions & 0 deletions crates/vm/src/stdlib/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,14 @@ mod decl {
use std::fmt::Write;

let instant = t.naive_or_local(vm)?;

// On Windows/AIX/Solaris, %y format with year < 1900 is not supported
#[cfg(any(windows, target_os = "aix", target_os = "solaris"))]
if instant.year() < 1900 && format.as_str().contains("%y") {
let msg = "format %y requires year >= 1900 on Windows";
return Err(vm.new_value_error(msg.to_owned()));
}

let mut formatted_time = String::new();

/*
Expand Down
Loading