mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 10:44:36 +08:00
Preserve interpreter-boundary diagnostics and syslog restrictions (#9365)
* Preserve Unicode in thread unraisable messages Keep the callable representation as WTF-8 for unraisable hooks and audit callbacks. Preserve the existing String entry point and stderr fallback; this does not claim lossless stderr rendering. Assisted-by: OpenAI Codex:model unavailable * Use the WTF-8 string conversion constructor Use the existing From<&str> implementation to copy the message prefix without changing Unicode semantics. Assisted-by: OpenAI Codex:model unavailable * Preserve cross-interpreter marshal error causes Wrap failures from existing code/function serialization helpers as NotShareableError, matching the CPython 3.15 marshal boundary while retaining the original exception cause. No new test C API or lifecycle behavior. Assisted-by: OpenAI Codex:model-unavailable * Enforce syslog interpreter boundaries and sync queue cleanup Reject subinterpreter openlog/closelog before audit or host mutation, and reject implicit syslog opening after audit when the process logger is not already open, matching CPython 3.15 guard placement and error text. Already-open logging and setlogmask remain unchanged. Migrate concurrent/interpreters/_queues.py through scripts/update_lib to CPython d220409a13b77de7a6c30abe16ea90db01528edc, removing unused eager pickle import and exception targets. Native serialization imports pickle on demand; module import timing and debug locals may nevertheless differ. No new fixtures or interpreter lifecycle mechanisms are introduced. Assisted-by: OpenAI Codex:model version unavailable
This commit is contained in:
1 parent
a90f7b9b9b
commit
1052e613ed
5 files changed
+45
-20
No files matched your search
+3
-4
@@ -1,6 +1,5 @@
|
||||
"""Cross-interpreter Queues High Level Module."""
|
||||
|
||||
import pickle
|
||||
import queue
|
||||
import time
|
||||
import weakref
|
||||
@@ -225,7 +224,7 @@ class Queue:
|
||||
while True:
|
||||
try:
|
||||
_queues.put(self._id, obj, unboundop)
|
||||
except QueueFull as exc:
|
||||
except QueueFull:
|
||||
if timeout is not None and time.time() >= end:
|
||||
raise # re-raise
|
||||
time.sleep(_delay)
|
||||
@@ -260,7 +259,7 @@ class Queue:
|
||||
while True:
|
||||
try:
|
||||
obj, unboundop = _queues.get(self._id)
|
||||
except QueueEmpty as exc:
|
||||
except QueueEmpty:
|
||||
if timeout is not None and time.time() >= end:
|
||||
raise # re-raise
|
||||
time.sleep(_delay)
|
||||
@@ -279,7 +278,7 @@ class Queue:
|
||||
"""
|
||||
try:
|
||||
obj, unboundop = _queues.get(self._id)
|
||||
except QueueEmpty as exc:
|
||||
except QueueEmpty:
|
||||
raise # re-raise
|
||||
if unboundop is not None:
|
||||
assert obj is None, repr(obj)
|
||||
|
||||
@@ -76,6 +76,9 @@ mod syslog {
|
||||
|
||||
#[pyfunction]
|
||||
fn openlog(args: OpenLogArgs, vm: &VirtualMachine) -> PyResult<()> {
|
||||
if !vm.state.is_main_interpreter() {
|
||||
return Err(vm.new_runtime_error("subinterpreter can't use syslog.openlog()"));
|
||||
}
|
||||
let logoption = args.logoption;
|
||||
let facility = args.facility;
|
||||
let ident = match args.ident.clone() {
|
||||
@@ -116,6 +119,11 @@ mod syslog {
|
||||
vm.audit("syslog.syslog", || (priority, msg.clone()))?;
|
||||
|
||||
if !host_syslog::is_open() {
|
||||
if !vm.state.is_main_interpreter() {
|
||||
return Err(vm.new_runtime_error(
|
||||
"subinterpreter can't use syslog.syslog() until the syslog is opened by the main interpreter",
|
||||
));
|
||||
}
|
||||
openlog(
|
||||
OpenLogArgs {
|
||||
facility: LOG_USER,
|
||||
@@ -132,6 +140,9 @@ mod syslog {
|
||||
|
||||
#[pyfunction]
|
||||
fn closelog(vm: &VirtualMachine) -> PyResult<()> {
|
||||
if !vm.state.is_main_interpreter() {
|
||||
return Err(vm.new_runtime_error("subinterpreter can't use syslog.closelog()"));
|
||||
}
|
||||
vm.audit("syslog.closelog", || ())?;
|
||||
|
||||
host_syslog::closelog();
|
||||
|
||||
@@ -805,12 +805,12 @@ pub(crate) mod _thread {
|
||||
func: &ArgCallable,
|
||||
vm: &VirtualMachine,
|
||||
) {
|
||||
let msg = func
|
||||
.as_ref()
|
||||
.repr(vm)
|
||||
.ok()
|
||||
.map(|repr| format!("Exception ignored in thread started by {}", repr.as_wtf8()));
|
||||
vm.run_unraisable(exc, msg, vm.ctx.none());
|
||||
let msg = func.as_ref().repr(vm).ok().map(|repr| {
|
||||
let mut message = Wtf8Buf::from("Exception ignored in thread started by ");
|
||||
message.push_wtf8(repr.as_wtf8());
|
||||
vm.ctx.new_str(message)
|
||||
});
|
||||
vm.run_unraisable_with_message(exc, msg, vm.ctx.none());
|
||||
}
|
||||
|
||||
fn run_thread(func: ArgCallable, args: FuncArgs, vm: &VirtualMachine) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//!
|
||||
//! Interpreters do not share `PyObject` graphs. Shareable values are converted
|
||||
//! to an interpreter-neutral payload and rebuilt in the destination.
|
||||
// cspell:ignore unpickler
|
||||
// cspell:ignore unpickler unmarshalled
|
||||
|
||||
#[cfg(not(feature = "threading"))]
|
||||
use crate::protocol::BufferDescriptor;
|
||||
@@ -351,17 +351,23 @@ impl SharedValue {
|
||||
}
|
||||
|
||||
fn marshal_dumps(obj: &PyObject, vm: &VirtualMachine) -> PyResult<Vec<u8>> {
|
||||
let dumps = vm.import("marshal", 0)?.get_attr("dumps", vm)?;
|
||||
let bytes = dumps.call((obj.to_owned(),), vm)?;
|
||||
let bytes = bytes
|
||||
.downcast::<PyBytes>()
|
||||
.map_err(|_| vm.new_type_error("marshal.dumps() did not return bytes"))?;
|
||||
Ok(bytes.as_bytes().to_vec())
|
||||
let dump = || -> PyResult<Vec<u8>> {
|
||||
let dumps = vm.import("marshal", 0)?.get_attr("dumps", vm)?;
|
||||
let bytes = dumps.call((obj.to_owned(),), vm)?;
|
||||
let bytes = bytes
|
||||
.downcast::<PyBytes>()
|
||||
.map_err(|_| vm.new_type_error("marshal.dumps() did not return bytes"))?;
|
||||
Ok(bytes.as_bytes().to_vec())
|
||||
};
|
||||
dump().map_err(|cause| not_shareable_error_from(vm, "object could not be marshalled", cause))
|
||||
}
|
||||
|
||||
fn marshal_loads(data: &[u8], vm: &VirtualMachine) -> PyResult {
|
||||
let loads = vm.import("marshal", 0)?.get_attr("loads", vm)?;
|
||||
loads.call((vm.ctx.new_bytes(data.to_vec()),), vm)
|
||||
let load = || -> PyResult {
|
||||
let loads = vm.import("marshal", 0)?.get_attr("loads", vm)?;
|
||||
loads.call((vm.ctx.new_bytes(data.to_vec()),), vm)
|
||||
};
|
||||
load().map_err(|cause| not_shareable_error_from(vm, "object could not be unmarshalled", cause))
|
||||
}
|
||||
|
||||
/// `_memoryview_from_xid`.
|
||||
|
||||
+10
-1
@@ -1816,7 +1816,16 @@ impl VirtualMachine {
|
||||
|
||||
#[cold]
|
||||
pub fn run_unraisable(&self, e: PyBaseExceptionRef, msg: Option<String>, object: PyObjectRef) {
|
||||
let msg = msg.map(|msg| self.ctx.new_str(msg));
|
||||
self.run_unraisable_with_message(e, msg.map(|msg| self.ctx.new_str(msg)), object);
|
||||
}
|
||||
|
||||
#[cold]
|
||||
pub(crate) fn run_unraisable_with_message(
|
||||
&self,
|
||||
e: PyBaseExceptionRef,
|
||||
msg: Option<PyStrRef>,
|
||||
object: PyObjectRef,
|
||||
) {
|
||||
let write_original = || {
|
||||
let msg = msg.as_ref().map(|msg| msg.to_string_lossy());
|
||||
self.write_unraisable_to_stderr(&e, msg.as_deref(), &object);
|
||||
|
||||
Reference in new issue
Block a user