mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
fix fcntl, sslsocket traverse, super , excepthook (#6623)
* fix fcntl * fix traverse * fix super * excepthook * fix warn
This commit is contained in:
2
Lib/test/test_super.py
vendored
2
Lib/test/test_super.py
vendored
@@ -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):
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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>>>,
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user