fix fcntl, sslsocket traverse, super , excepthook (#6623)

* fix fcntl

* fix traverse

* fix super

* excepthook

* fix warn
This commit is contained in:
Jeong, YunWon
2026-01-03 00:15:31 +09:00
committed by GitHub
parent 546d35b8c1
commit 0eddee500a
6 changed files with 166 additions and 7 deletions

View File

@@ -344,7 +344,6 @@ class TestSuper(unittest.TestCase):
with self.assertRaisesRegex(TypeError, "expected at most"):
super(int, int, int)
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: "argument 1 must be a type" does not match "Expected type 'type' but 'int' found."
def test_super_argtype(self):
with self.assertRaisesRegex(TypeError, "argument 1 must be a type"):
super(1, int)
@@ -409,7 +408,6 @@ class TestSuper(unittest.TestCase):
with self.assertRaisesRegex(AttributeError, "'super' object has no attribute 'msg'"):
C().method()
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: "argument 1 must be a type" does not match "Expected type 'type' but 'int' found."
def test_bad_first_arg(self):
class C:
def method(self):

View File

@@ -92,11 +92,15 @@ mod fcntl {
#[pyfunction]
fn ioctl(
io::Fildes(fd): io::Fildes,
request: u32,
request: i64,
arg: OptionalArg<Either<Either<ArgMemoryBuffer, ArgStrOrBytesLike>, i32>>,
mutate_flag: OptionalArg<bool>,
vm: &VirtualMachine,
) -> PyResult {
// Convert to unsigned - handles both positive u32 values and negative i32 values
// that represent the same bit pattern (e.g., TIOCSWINSZ on some platforms).
// First truncate to u32 (takes lower 32 bits), then zero-extend to c_ulong.
let request = (request as u32) as libc::c_ulong;
let arg = arg.unwrap_or_else(|| Either::B(0));
match arg {
Either::A(buf_kind) => {

View File

@@ -2293,7 +2293,7 @@ mod _ssl {
// SSLSocket - represents a TLS-wrapped socket
#[pyattr]
#[pyclass(name = "_SSLSocket", module = "ssl")]
#[pyclass(name = "_SSLSocket", module = "ssl", traverse)]
#[derive(Debug, PyPayload)]
pub(crate) struct PySSLSocket {
// Underlying socket
@@ -2301,14 +2301,19 @@ mod _ssl {
// SSL context
context: PyRwLock<PyRef<PySSLContext>>,
// Server-side or client-side
#[pytraverse(skip)]
server_side: bool,
// Server hostname for SNI
#[pytraverse(skip)]
server_hostname: PyRwLock<Option<String>>,
// TLS connection state
#[pytraverse(skip)]
connection: PyMutex<Option<TlsConnection>>,
// Handshake completed flag
#[pytraverse(skip)]
handshake_done: PyMutex<bool>,
// Session was reused (for session resumption tracking)
#[pytraverse(skip)]
session_was_reused: PyMutex<bool>,
// Owner (SSLSocket instance that owns this _SSLSocket)
owner: PyRwLock<Option<PyObjectRef>>,
@@ -2316,22 +2321,27 @@ mod _ssl {
session: PyRwLock<Option<PyObjectRef>>,
// Verified certificate chain (built during verification)
#[allow(dead_code)]
#[pytraverse(skip)]
verified_chain: PyRwLock<Option<Vec<CertificateDer<'static>>>>,
// MemoryBIO mode (optional)
incoming_bio: Option<PyRef<PyMemoryBIO>>,
outgoing_bio: Option<PyRef<PyMemoryBIO>>,
// SNI certificate resolver state (for server-side only)
#[pytraverse(skip)]
sni_state: PyRwLock<Option<Arc<ParkingMutex<SniCertName>>>>,
// Pending context change (for SNI callback deferred handling)
pending_context: PyRwLock<Option<PyRef<PySSLContext>>>,
// Buffer to store ClientHello for connection recreation
#[pytraverse(skip)]
client_hello_buffer: PyMutex<Option<Vec<u8>>>,
// Shutdown state for tracking close-notify exchange
#[pytraverse(skip)]
shutdown_state: PyMutex<ShutdownState>,
// Deferred client certificate verification error (for TLS 1.3)
// Stores error message if client cert verification failed during handshake
// Error is raised on first I/O operation after handshake
// Using Arc to share with the certificate verifier
#[pytraverse(skip)]
deferred_cert_error: Arc<ParkingRwLock<Option<String>>>,
}

View File

@@ -61,7 +61,7 @@ impl Constructor for PySuper {
#[derive(FromArgs)]
pub struct InitArgs {
#[pyarg(positional, optional)]
py_type: OptionalArg<PyTypeRef>,
py_type: OptionalArg<PyObjectRef>,
#[pyarg(positional, optional)]
py_obj: OptionalArg<PyObjectRef>,
}
@@ -75,7 +75,10 @@ impl Initializer for PySuper {
vm: &VirtualMachine,
) -> PyResult<()> {
// Get the type:
let (typ, obj) = if let OptionalArg::Present(ty) = py_type {
let (typ, obj) = if let OptionalArg::Present(ty_obj) = py_type {
let ty = ty_obj
.downcast::<PyType>()
.map_err(|_| vm.new_type_error("super() argument 1 must be a type"))?;
(ty, py_obj.unwrap_or_none(vm))
} else {
let frame = vm

View File

@@ -400,6 +400,138 @@ pub(crate) mod _thread {
vm.state.thread_count.load()
}
/// ExceptHookArgs - simple class to hold exception hook arguments
/// This allows threading.py to import _excepthook and _ExceptHookArgs from _thread
#[pyattr]
#[pyclass(module = "_thread", name = "_ExceptHookArgs")]
#[derive(Debug, PyPayload)]
struct ExceptHookArgs {
exc_type: crate::PyObjectRef,
exc_value: crate::PyObjectRef,
exc_traceback: crate::PyObjectRef,
thread: crate::PyObjectRef,
}
#[pyclass(with(Constructor))]
impl ExceptHookArgs {
#[pygetset]
fn exc_type(&self) -> crate::PyObjectRef {
self.exc_type.clone()
}
#[pygetset]
fn exc_value(&self) -> crate::PyObjectRef {
self.exc_value.clone()
}
#[pygetset]
fn exc_traceback(&self) -> crate::PyObjectRef {
self.exc_traceback.clone()
}
#[pygetset]
fn thread(&self) -> crate::PyObjectRef {
self.thread.clone()
}
}
impl Constructor for ExceptHookArgs {
// Takes a single iterable argument like namedtuple
type Args = (crate::PyObjectRef,);
fn py_new(_cls: &Py<PyType>, args: Self::Args, vm: &VirtualMachine) -> PyResult<Self> {
// Convert the argument to a list/tuple and extract elements
let seq: Vec<crate::PyObjectRef> = args.0.try_to_value(vm)?;
if seq.len() != 4 {
return Err(vm.new_type_error(format!(
"_ExceptHookArgs expected 4 arguments, got {}",
seq.len()
)));
}
Ok(Self {
exc_type: seq[0].clone(),
exc_value: seq[1].clone(),
exc_traceback: seq[2].clone(),
thread: seq[3].clone(),
})
}
}
/// Handle uncaught exception in Thread.run()
#[pyfunction]
fn _excepthook(args: crate::PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
// Type check: args must be _ExceptHookArgs
let args = args.downcast::<ExceptHookArgs>().map_err(|_| {
vm.new_type_error(
"_thread._excepthook argument type must be _ExceptHookArgs".to_owned(),
)
})?;
let exc_type = args.exc_type.clone();
let exc_value = args.exc_value.clone();
let exc_traceback = args.exc_traceback.clone();
let thread = args.thread.clone();
// Silently ignore SystemExit (identity check)
if exc_type.is(vm.ctx.exceptions.system_exit.as_ref()) {
return Ok(());
}
// Get stderr - fall back to thread._stderr if sys.stderr is None
let file = match vm.sys_module.get_attr("stderr", vm) {
Ok(stderr) if !vm.is_none(&stderr) => stderr,
_ => {
if vm.is_none(&thread) {
// do nothing if sys.stderr is None and thread is None
return Ok(());
}
let thread_stderr = thread.get_attr("_stderr", vm)?;
if vm.is_none(&thread_stderr) {
// do nothing if sys.stderr is None and sys.stderr was None
// when the thread was created
return Ok(());
}
thread_stderr
}
};
// Print "Exception in thread {thread.name}:"
let thread_name = if !vm.is_none(&thread) {
thread
.get_attr("name", vm)
.ok()
.and_then(|n| n.str(vm).ok())
.map(|s| s.as_str().to_owned())
} else {
None
};
let name = thread_name.unwrap_or_else(|| format!("{}", get_ident()));
let _ = vm.call_method(
&file,
"write",
(format!("Exception in thread {}:\n", name),),
);
// Display the traceback
if let Ok(traceback_mod) = vm.import("traceback", 0)
&& let Ok(print_exc) = traceback_mod.get_attr("print_exception", vm)
{
use crate::function::KwArgs;
let kwargs: KwArgs = vec![("file".to_owned(), file.clone())]
.into_iter()
.collect();
let _ = print_exc.call_with_args(
crate::function::FuncArgs::new(vec![exc_type, exc_value, exc_traceback], kwargs),
vm,
);
}
// Flush file
let _ = vm.call_method(&file, "flush", ());
Ok(())
}
#[pyattr]
#[pyclass(module = "thread", name = "_local")]
#[derive(Debug, PyPayload)]

View File

@@ -404,8 +404,20 @@ fn setup_context(
let (globals, filename, lineno) = if let Some(f) = f {
(f.globals.clone(), f.code.source_path, f.f_lineno())
} else if let Some(frame) = vm.current_frame() {
// We have a frame but it wasn't found during stack walking
(frame.globals.clone(), vm.ctx.intern_str("<sys>"), 1)
} else {
(vm.current_globals().clone(), vm.ctx.intern_str("sys"), 1)
// No frames on the stack - use sys.__dict__ (interp->sysdict)
let globals = vm
.sys_module
.as_object()
.get_attr(identifier!(vm, __dict__), vm)
.and_then(|d| {
d.downcast::<crate::builtins::PyDict>()
.map_err(|_| vm.new_type_error("sys.__dict__ is not a dictionary"))
})?;
(globals, vm.ctx.intern_str("<sys>"), 0)
};
let registry = if let Ok(registry) = globals.get_item(__warningregistry__, vm) {