1#[cfg(feature = "rustpython-compiler")]
7mod compile;
8mod context;
9mod interpreter;
10mod method;
11#[cfg(feature = "rustpython-compiler")]
12mod python_run;
13mod setting;
14pub mod thread;
15mod vm_new;
16mod vm_object;
17mod vm_ops;
18
19use crate::{
20 AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
21 builtins::{
22 self, PyBaseExceptionRef, PyDict, PyDictRef, PyInt, PyList, PyModule, PyStr, PyStrInterned,
23 PyStrRef, PyTypeRef, PyUtf8Str, PyUtf8StrInterned, PyWeak,
24 code::PyCode,
25 dict::{PyDictItems, PyDictKeys, PyDictValues},
26 pystr::AsPyStr,
27 tuple::PyTuple,
28 },
29 codecs::CodecsRegistry,
30 common::{hash::HashSecret, lock::PyMutex, rc::PyRc},
31 convert::ToPyObject,
32 exceptions::types::PyBaseException,
33 frame::{ExecutionResult, Frame, FrameRef},
34 frozen::FrozenModule,
35 function::{ArgMapping, FuncArgs, PySetterValue},
36 import,
37 protocol::PyIterIter,
38 scope::Scope,
39 signal, stdlib,
40 warn::WarningsState,
41};
42use alloc::{borrow::Cow, collections::BTreeMap};
43#[cfg(all(unix, feature = "threading"))]
44use core::sync::atomic::AtomicI64;
45use core::{
46 cell::{Cell, OnceCell, RefCell},
47 ptr::NonNull,
48 sync::atomic::{AtomicBool, AtomicU64, Ordering},
49};
50use crossbeam_utils::atomic::AtomicCell;
51#[cfg(unix)]
52use nix::{
53 sys::signal::{SaFlags, SigAction, SigSet, Signal::SIGINT, kill, sigaction},
54 unistd::getpid,
55};
56use std::{
57 collections::{HashMap, HashSet},
58 ffi::{OsStr, OsString},
59};
60
61pub use context::Context;
62pub use interpreter::{Interpreter, InterpreterBuilder};
63pub(crate) use method::PyMethod;
64pub use setting::{CheckHashPycsMode, Paths, PyConfig, Settings};
65
66pub const MAX_MEMORY_SIZE: usize = isize::MAX as usize;
67
68pub struct VirtualMachine {
75 pub builtins: PyRef<PyModule>,
76 pub sys_module: PyRef<PyModule>,
77 pub ctx: PyRc<Context>,
78 pub frames: RefCell<Vec<FramePtr>>,
79 datastack: core::cell::UnsafeCell<crate::datastack::DataStack>,
82 pub wasm_id: Option<String>,
83 exceptions: RefCell<ExceptionStack>,
84 pub import_func: PyObjectRef,
85 pub(crate) importlib: PyObjectRef,
86 pub profile_func: RefCell<PyObjectRef>,
87 pub trace_func: RefCell<PyObjectRef>,
88 pub use_tracing: Cell<bool>,
89 pub recursion_limit: Cell<usize>,
90 pub(crate) signal_handlers: OnceCell<Box<RefCell<[Option<PyObjectRef>; signal::NSIG]>>>,
91 pub(crate) signal_rx: Option<signal::UserSignalReceiver>,
92 pub repr_guards: RefCell<HashSet<usize>>,
93 pub state: PyRc<PyGlobalState>,
94 pub initialized: bool,
95 recursion_depth: Cell<usize>,
96 #[cfg_attr(miri, allow(dead_code))]
98 c_stack_soft_limit: Cell<usize>,
99 pub async_gen_firstiter: RefCell<Option<PyObjectRef>>,
101 pub async_gen_finalizer: RefCell<Option<PyObjectRef>>,
103 pub asyncio_running_loop: RefCell<Option<PyObjectRef>>,
105 pub asyncio_running_task: RefCell<Option<PyObjectRef>>,
107 pub(crate) callable_cache: CallableCache,
108}
109
110#[derive(Copy, Clone)]
113pub struct FramePtr(NonNull<Py<Frame>>);
114
115impl FramePtr {
116 pub unsafe fn as_ref(&self) -> &Py<Frame> {
119 unsafe { self.0.as_ref() }
120 }
121}
122
123unsafe impl Send for FramePtr {}
126
127#[derive(Debug, Default)]
128struct ExceptionStack {
129 stack: Vec<Option<PyBaseExceptionRef>>,
130}
131
132#[cfg(all(unix, feature = "threading"))]
135pub struct StopTheWorldState {
136 pub(crate) requested: AtomicBool,
138 world_stopped: AtomicBool,
140 requester: AtomicU64,
142 notify_mutex: std::sync::Mutex<()>,
144 notify_cv: std::sync::Condvar,
145 thread_countdown: AtomicI64,
147 stats_stop_calls: AtomicU64,
149 stats_last_wait_ns: AtomicU64,
151 stats_total_wait_ns: AtomicU64,
153 stats_max_wait_ns: AtomicU64,
155 stats_poll_loops: AtomicU64,
157 stats_attached_seen: AtomicU64,
159 stats_forced_parks: AtomicU64,
161 stats_suspend_notifications: AtomicU64,
163 stats_attach_wait_yields: AtomicU64,
165 stats_suspend_wait_yields: AtomicU64,
167}
168
169#[cfg(all(unix, feature = "threading"))]
170#[derive(Debug, Clone, Copy)]
171pub struct StopTheWorldStats {
172 pub stop_calls: u64,
173 pub last_wait_ns: u64,
174 pub total_wait_ns: u64,
175 pub max_wait_ns: u64,
176 pub poll_loops: u64,
177 pub attached_seen: u64,
178 pub forced_parks: u64,
179 pub suspend_notifications: u64,
180 pub attach_wait_yields: u64,
181 pub suspend_wait_yields: u64,
182 pub world_stopped: bool,
183}
184
185#[cfg(all(unix, feature = "threading"))]
186impl Default for StopTheWorldState {
187 fn default() -> Self {
188 Self::new()
189 }
190}
191
192#[cfg(all(unix, feature = "threading"))]
193impl StopTheWorldState {
194 pub const fn new() -> Self {
195 Self {
196 requested: AtomicBool::new(false),
197 world_stopped: AtomicBool::new(false),
198 requester: AtomicU64::new(0),
199 notify_mutex: std::sync::Mutex::new(()),
200 notify_cv: std::sync::Condvar::new(),
201 thread_countdown: AtomicI64::new(0),
202 stats_stop_calls: AtomicU64::new(0),
203 stats_last_wait_ns: AtomicU64::new(0),
204 stats_total_wait_ns: AtomicU64::new(0),
205 stats_max_wait_ns: AtomicU64::new(0),
206 stats_poll_loops: AtomicU64::new(0),
207 stats_attached_seen: AtomicU64::new(0),
208 stats_forced_parks: AtomicU64::new(0),
209 stats_suspend_notifications: AtomicU64::new(0),
210 stats_attach_wait_yields: AtomicU64::new(0),
211 stats_suspend_wait_yields: AtomicU64::new(0),
212 }
213 }
214
215 pub(crate) fn notify_suspended(&self) {
217 self.stats_suspend_notifications
218 .fetch_add(1, Ordering::Relaxed);
219 let _guard = self.notify_mutex.lock().unwrap();
221 self.decrement_thread_countdown(1);
222 self.notify_cv.notify_one();
223 }
224
225 #[inline]
226 fn init_thread_countdown(&self, vm: &VirtualMachine) -> i64 {
227 let requester = self.requester.load(Ordering::Relaxed);
228 let registry = vm.state.thread_frames.lock();
229 self.requested.store(true, Ordering::Release);
233 let count = registry
234 .keys()
235 .filter(|&&thread_id| thread_id != requester)
236 .count();
237 let count = (count.min(i64::MAX as usize)) as i64;
238 self.thread_countdown.store(count, Ordering::Release);
239 count
240 }
241
242 #[inline]
243 fn decrement_thread_countdown(&self, n: u64) {
244 if n == 0 {
245 return;
246 }
247 let n = (n.min(i64::MAX as u64)) as i64;
248 let prev = self.thread_countdown.fetch_sub(n, Ordering::AcqRel);
249 if prev <= n {
250 self.thread_countdown.store(0, Ordering::Release);
252 }
253 }
254
255 fn park_detached_threads(&self, vm: &VirtualMachine) -> bool {
258 use thread::{THREAD_ATTACHED, THREAD_DETACHED, THREAD_SUSPENDED};
259 let requester = self.requester.load(Ordering::Relaxed);
260 let registry = vm.state.thread_frames.lock();
261 let mut attached_seen = 0u64;
262 let mut forced_parks = 0u64;
263 for (&id, slot) in registry.iter() {
264 if id == requester {
265 continue;
266 }
267 let state = slot.state.load(Ordering::Relaxed);
268 if state == THREAD_DETACHED {
269 match slot.state.compare_exchange(
271 THREAD_DETACHED,
272 THREAD_SUSPENDED,
273 Ordering::AcqRel,
274 Ordering::Relaxed,
275 ) {
276 Ok(_) => {
277 slot.stop_requested.store(false, Ordering::Release);
278 forced_parks = forced_parks.saturating_add(1);
279 }
280 Err(THREAD_ATTACHED) => {
281 slot.stop_requested.store(true, Ordering::Release);
283 attached_seen = attached_seen.saturating_add(1);
285 }
286 Err(THREAD_DETACHED) => {
287 }
289 Err(THREAD_SUSPENDED) => {
290 slot.stop_requested.store(false, Ordering::Release);
291 }
293 Err(other) => {
294 debug_assert!(
295 false,
296 "unexpected thread state in park_detached_threads: {other}"
297 );
298 }
299 }
300 } else if state == THREAD_ATTACHED {
301 slot.stop_requested.store(true, Ordering::Release);
303 attached_seen = attached_seen.saturating_add(1);
305 }
306 }
308 if attached_seen != 0 {
309 self.stats_attached_seen
310 .fetch_add(attached_seen, Ordering::Relaxed);
311 }
312 if forced_parks != 0 {
313 self.decrement_thread_countdown(forced_parks);
314 self.stats_forced_parks
315 .fetch_add(forced_parks, Ordering::Relaxed);
316 }
317 forced_parks != 0 && self.thread_countdown.load(Ordering::Acquire) == 0
318 }
319
320 pub fn stop_the_world(&self, vm: &VirtualMachine) {
327 let start = std::time::Instant::now();
328 let requester_ident = crate::stdlib::_thread::get_ident();
329 self.requester.store(requester_ident, Ordering::Relaxed);
330 self.stats_stop_calls.fetch_add(1, Ordering::Relaxed);
331 let initial_countdown = self.init_thread_countdown(vm);
332 stw_trace(format_args!("stop begin requester={requester_ident}"));
333 if initial_countdown == 0 {
334 self.world_stopped.store(true, Ordering::Release);
335 #[cfg(debug_assertions)]
336 self.debug_assert_all_non_requester_suspended(vm);
337 stw_trace(format_args!(
338 "stop end requester={requester_ident} wait_ns=0 polls=0"
339 ));
340 return;
341 }
342
343 let mut polls = 0u64;
344 loop {
345 if self.park_detached_threads(vm) {
346 break;
347 }
348 polls = polls.saturating_add(1);
349 let guard = self.notify_mutex.lock().unwrap();
353 if self.thread_countdown.load(Ordering::Acquire) == 0 || self.park_detached_threads(vm)
354 {
355 drop(guard);
356 break;
357 }
358 let _ = self
359 .notify_cv
360 .wait_timeout(guard, core::time::Duration::from_millis(1));
361 }
362 if polls != 0 {
363 self.stats_poll_loops.fetch_add(polls, Ordering::Relaxed);
364 }
365 let wait_ns = start.elapsed().as_nanos().min(u128::from(u64::MAX)) as u64;
366 self.stats_last_wait_ns.store(wait_ns, Ordering::Relaxed);
367 self.stats_total_wait_ns
368 .fetch_add(wait_ns, Ordering::Relaxed);
369 let mut prev_max = self.stats_max_wait_ns.load(Ordering::Relaxed);
370 while wait_ns > prev_max {
371 match self.stats_max_wait_ns.compare_exchange_weak(
372 prev_max,
373 wait_ns,
374 Ordering::Relaxed,
375 Ordering::Relaxed,
376 ) {
377 Ok(_) => break,
378 Err(observed) => prev_max = observed,
379 }
380 }
381 self.world_stopped.store(true, Ordering::Release);
382 #[cfg(debug_assertions)]
383 self.debug_assert_all_non_requester_suspended(vm);
384 stw_trace(format_args!(
385 "stop end requester={requester_ident} wait_ns={wait_ns} polls={polls}"
386 ));
387 }
388
389 pub fn start_the_world(&self, vm: &VirtualMachine) {
391 use thread::{THREAD_DETACHED, THREAD_SUSPENDED};
392 let requester = self.requester.load(Ordering::Relaxed);
393 stw_trace(format_args!("start begin requester={requester}"));
394 let registry = vm.state.thread_frames.lock();
395 self.requested.store(false, Ordering::Release);
401 self.world_stopped.store(false, Ordering::Release);
402 for (&id, slot) in registry.iter() {
403 if id == requester {
404 continue;
405 }
406 slot.stop_requested.store(false, Ordering::Release);
407 let state = slot.state.load(Ordering::Relaxed);
408 debug_assert!(
409 state == THREAD_SUSPENDED,
410 "non-requester thread not suspended at start-the-world: id={id} state={state}"
411 );
412 if state == THREAD_SUSPENDED {
413 slot.state.store(THREAD_DETACHED, Ordering::Release);
414 slot.thread.unpark();
415 }
416 }
417 drop(registry);
418 self.thread_countdown.store(0, Ordering::Release);
419 self.requester.store(0, Ordering::Relaxed);
420 #[cfg(debug_assertions)]
421 self.debug_assert_all_non_requester_detached(vm);
422 stw_trace(format_args!("start end requester={requester}"));
423 }
424
425 pub fn reset_after_fork(&self) {
427 self.requested.store(false, Ordering::Relaxed);
428 self.world_stopped.store(false, Ordering::Relaxed);
429 self.requester.store(0, Ordering::Relaxed);
430 self.thread_countdown.store(0, Ordering::Relaxed);
431 stw_trace(format_args!("reset-after-fork"));
432 }
433
434 #[inline]
435 pub(crate) fn requester_ident(&self) -> u64 {
436 self.requester.load(Ordering::Relaxed)
437 }
438
439 #[inline]
440 pub(crate) fn notify_thread_gone(&self) {
441 let _guard = self.notify_mutex.lock().unwrap();
442 self.decrement_thread_countdown(1);
443 self.notify_cv.notify_one();
444 }
445
446 pub fn stats_snapshot(&self) -> StopTheWorldStats {
447 StopTheWorldStats {
448 stop_calls: self.stats_stop_calls.load(Ordering::Relaxed),
449 last_wait_ns: self.stats_last_wait_ns.load(Ordering::Relaxed),
450 total_wait_ns: self.stats_total_wait_ns.load(Ordering::Relaxed),
451 max_wait_ns: self.stats_max_wait_ns.load(Ordering::Relaxed),
452 poll_loops: self.stats_poll_loops.load(Ordering::Relaxed),
453 attached_seen: self.stats_attached_seen.load(Ordering::Relaxed),
454 forced_parks: self.stats_forced_parks.load(Ordering::Relaxed),
455 suspend_notifications: self.stats_suspend_notifications.load(Ordering::Relaxed),
456 attach_wait_yields: self.stats_attach_wait_yields.load(Ordering::Relaxed),
457 suspend_wait_yields: self.stats_suspend_wait_yields.load(Ordering::Relaxed),
458 world_stopped: self.world_stopped.load(Ordering::Relaxed),
459 }
460 }
461
462 pub fn reset_stats(&self) {
463 self.stats_stop_calls.store(0, Ordering::Relaxed);
464 self.stats_last_wait_ns.store(0, Ordering::Relaxed);
465 self.stats_total_wait_ns.store(0, Ordering::Relaxed);
466 self.stats_max_wait_ns.store(0, Ordering::Relaxed);
467 self.stats_poll_loops.store(0, Ordering::Relaxed);
468 self.stats_attached_seen.store(0, Ordering::Relaxed);
469 self.stats_forced_parks.store(0, Ordering::Relaxed);
470 self.stats_suspend_notifications.store(0, Ordering::Relaxed);
471 self.stats_attach_wait_yields.store(0, Ordering::Relaxed);
472 self.stats_suspend_wait_yields.store(0, Ordering::Relaxed);
473 }
474
475 #[inline]
476 pub(crate) fn add_attach_wait_yields(&self, n: u64) {
477 if n != 0 {
478 self.stats_attach_wait_yields
479 .fetch_add(n, Ordering::Relaxed);
480 }
481 }
482
483 #[inline]
484 pub(crate) fn add_suspend_wait_yields(&self, n: u64) {
485 if n != 0 {
486 self.stats_suspend_wait_yields
487 .fetch_add(n, Ordering::Relaxed);
488 }
489 }
490
491 #[cfg(debug_assertions)]
492 fn debug_assert_all_non_requester_suspended(&self, vm: &VirtualMachine) {
493 use thread::THREAD_SUSPENDED;
494 let requester = self.requester.load(Ordering::Relaxed);
495 let registry = vm.state.thread_frames.lock();
496 for (&id, slot) in registry.iter() {
497 if id == requester {
498 continue;
499 }
500 let state = slot.state.load(Ordering::Relaxed);
501 debug_assert!(
502 state == THREAD_SUSPENDED,
503 "non-requester thread not suspended during stop-the-world: id={id} state={state}"
504 );
505 }
506 }
507
508 #[cfg(debug_assertions)]
509 fn debug_assert_all_non_requester_detached(&self, vm: &VirtualMachine) {
510 use thread::THREAD_SUSPENDED;
511 let requester = self.requester.load(Ordering::Relaxed);
512 let registry = vm.state.thread_frames.lock();
513 for (&id, slot) in registry.iter() {
514 if id == requester {
515 continue;
516 }
517 let state = slot.state.load(Ordering::Relaxed);
518 debug_assert!(
519 state != THREAD_SUSPENDED,
520 "non-requester thread still suspended after start-the-world: id={id} state={state}"
521 );
522 }
523 }
524}
525
526#[cfg(all(unix, feature = "threading"))]
527pub(super) fn stw_trace_enabled() -> bool {
528 static ENABLED: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
529 *ENABLED.get_or_init(|| std::env::var_os("RUSTPYTHON_STW_TRACE").is_some())
530}
531
532#[cfg(all(unix, feature = "threading"))]
533pub(super) fn stw_trace(msg: core::fmt::Arguments<'_>) {
534 if stw_trace_enabled() {
535 use core::fmt::Write as _;
536
537 struct FixedBuf {
540 buf: [u8; 512],
541 len: usize,
542 }
543
544 impl core::fmt::Write for FixedBuf {
545 fn write_str(&mut self, s: &str) -> core::fmt::Result {
546 if self.len >= self.buf.len() {
547 return Ok(());
548 }
549 let remain = self.buf.len() - self.len;
550 let src = s.as_bytes();
551 let n = src.len().min(remain);
552 self.buf[self.len..self.len + n].copy_from_slice(&src[..n]);
553 self.len += n;
554 Ok(())
555 }
556 }
557
558 let mut out = FixedBuf {
559 buf: [0u8; 512],
560 len: 0,
561 };
562 let _ = writeln!(
563 &mut out,
564 "[rp-stw tid={}] {}",
565 crate::stdlib::_thread::get_ident(),
566 msg
567 );
568 unsafe {
569 let _ = libc::write(libc::STDERR_FILENO, out.buf.as_ptr().cast(), out.len);
570 }
571 }
572}
573
574#[derive(Clone, Debug, Default)]
575pub(crate) struct CallableCache {
576 pub len: Option<PyObjectRef>,
577 pub isinstance: Option<PyObjectRef>,
578 pub list_append: Option<PyObjectRef>,
579 pub builtin_all: Option<PyObjectRef>,
580 pub builtin_any: Option<PyObjectRef>,
581}
582
583pub struct PyGlobalState {
584 pub config: PyConfig,
585 pub module_defs: BTreeMap<&'static str, &'static builtins::PyModuleDef>,
586 pub frozen: HashMap<&'static str, FrozenModule, ahash::RandomState>,
587 pub stacksize: AtomicCell<usize>,
588 pub thread_count: AtomicCell<usize>,
589 pub hash_secret: HashSecret,
590 pub atexit_funcs: PyMutex<Vec<Box<(PyObjectRef, FuncArgs)>>>,
591 pub codec_registry: CodecsRegistry,
592 pub finalizing: AtomicBool,
593 pub warnings: WarningsState,
594 pub override_frozen_modules: AtomicCell<isize>,
595 pub before_forkers: PyMutex<Vec<PyObjectRef>>,
596 pub after_forkers_child: PyMutex<Vec<PyObjectRef>>,
597 pub after_forkers_parent: PyMutex<Vec<PyObjectRef>>,
598 pub int_max_str_digits: AtomicCell<usize>,
599 pub switch_interval: AtomicCell<f64>,
600 pub global_trace_func: PyMutex<Option<PyObjectRef>>,
602 pub global_profile_func: PyMutex<Option<PyObjectRef>>,
604 #[cfg(feature = "threading")]
606 pub main_thread_ident: AtomicCell<u64>,
607 #[cfg(feature = "threading")]
609 pub thread_frames: parking_lot::Mutex<HashMap<u64, stdlib::_thread::CurrentFrameSlot>>,
610 #[cfg(feature = "threading")]
612 pub thread_handles: parking_lot::Mutex<Vec<stdlib::_thread::HandleEntry>>,
613 #[cfg(feature = "threading")]
615 pub shutdown_handles: parking_lot::Mutex<Vec<stdlib::_thread::ShutdownEntry>>,
616 pub monitoring: PyMutex<stdlib::sys::monitoring::MonitoringState>,
618 pub monitoring_events: stdlib::sys::monitoring::MonitoringEventsMask,
620 pub instrumentation_version: AtomicU64,
623 #[cfg(all(unix, feature = "threading"))]
625 pub stop_the_world: StopTheWorldState,
626}
627
628pub fn process_hash_secret_seed() -> u32 {
629 use std::sync::OnceLock;
630 static SEED: OnceLock<u32> = OnceLock::new();
631 *SEED.get_or_init(|| u32::from_ne_bytes(rustpython_common::rand::os_random()))
633}
634
635impl VirtualMachine {
636 fn init_callable_cache(&mut self) -> PyResult<()> {
637 self.callable_cache.len = Some(self.builtins.get_attr("len", self)?);
638 self.callable_cache.isinstance = Some(self.builtins.get_attr("isinstance", self)?);
639 let list_append = self
640 .ctx
641 .types
642 .list_type
643 .get_attr(self.ctx.intern_str("append"))
644 .ok_or_else(|| self.new_runtime_error("failed to cache list.append".to_owned()))?;
645 self.callable_cache.list_append = Some(list_append);
646 self.callable_cache.builtin_all = Some(self.builtins.get_attr("all", self)?);
647 self.callable_cache.builtin_any = Some(self.builtins.get_attr("any", self)?);
648 Ok(())
649 }
650
651 #[inline(always)]
656 pub(crate) fn datastack_push(&self, size: usize) -> *mut u8 {
657 unsafe { (*self.datastack.get()).push(size) }
658 }
659
660 #[inline(always)]
662 pub(crate) fn datastack_has_space(&self, size: usize) -> bool {
663 unsafe { (*self.datastack.get()).has_space(size) }
664 }
665
666 #[inline(always)]
672 pub(crate) unsafe fn datastack_pop(&self, base: *mut u8) {
673 unsafe { (*self.datastack.get()).pop(base) }
674 }
675
676 #[inline]
682 pub fn allow_threads<R>(&self, f: impl FnOnce() -> R) -> R {
683 thread::allow_threads(self, f)
684 }
685
686 #[allow(dead_code)]
689 pub(crate) fn is_main_thread(&self) -> bool {
690 #[cfg(feature = "threading")]
691 {
692 crate::stdlib::_thread::get_ident() == self.state.main_thread_ident.load()
693 }
694 #[cfg(not(feature = "threading"))]
695 {
696 true
697 }
698 }
699
700 pub(crate) fn new(ctx: PyRc<Context>, state: PyRc<PyGlobalState>) -> Self {
702 flame_guard!("new VirtualMachine");
703
704 let new_module = |def| {
707 PyRef::new_ref(
708 PyModule::from_def(def),
709 ctx.types.module_type.to_owned(),
710 Some(ctx.new_dict()),
711 )
712 };
713
714 let builtins = new_module(stdlib::builtins::module_def(&ctx));
716 let sys_module = new_module(stdlib::sys::module_def(&ctx));
717
718 let import_func = ctx.none();
719 let importlib = ctx.none();
720 let profile_func = RefCell::new(ctx.none());
721 let trace_func = RefCell::new(ctx.none());
722 let signal_handlers = OnceCell::from(signal::new_signal_handlers());
723
724 let vm = Self {
725 builtins,
726 sys_module,
727 ctx,
728 frames: RefCell::new(vec![]),
729 datastack: core::cell::UnsafeCell::new(crate::datastack::DataStack::new()),
730 wasm_id: None,
731 exceptions: RefCell::default(),
732 import_func,
733 importlib,
734 profile_func,
735 trace_func,
736 use_tracing: Cell::new(false),
737 recursion_limit: Cell::new(if cfg!(debug_assertions) { 256 } else { 1000 }),
738 signal_handlers,
739 signal_rx: None,
740 repr_guards: RefCell::default(),
741 state,
742 initialized: false,
743 recursion_depth: Cell::new(0),
744 c_stack_soft_limit: Cell::new(Self::calculate_c_stack_soft_limit()),
745 async_gen_firstiter: RefCell::new(None),
746 async_gen_finalizer: RefCell::new(None),
747 asyncio_running_loop: RefCell::new(None),
748 asyncio_running_task: RefCell::new(None),
749 callable_cache: CallableCache::default(),
750 };
751
752 if vm.state.hash_secret.hash_str("")
753 != vm
754 .ctx
755 .interned_str("")
756 .expect("empty str must be interned")
757 .hash(&vm)
758 {
759 panic!("Interpreters in same process must share the hash seed");
760 }
761
762 vm.builtins.init_dict(
763 vm.ctx.intern_str("builtins"),
764 Some(vm.ctx.intern_str(stdlib::builtins::DOC.unwrap()).to_owned()),
765 &vm,
766 );
767 vm.sys_module.init_dict(
768 vm.ctx.intern_str("sys"),
769 Some(vm.ctx.intern_str(stdlib::sys::DOC.unwrap()).to_owned()),
770 &vm,
771 );
772 vm
774 }
775
776 #[cfg(feature = "encodings")]
779 fn import_encodings(&mut self) -> PyResult<()> {
780 self.import("encodings", 0).map_err(|import_err| {
781 let rustpythonpath_env = std::env::var("RUSTPYTHONPATH").ok();
782 let pythonpath_env = std::env::var("PYTHONPATH").ok();
783 let env_set = rustpythonpath_env.as_ref().is_some() || pythonpath_env.as_ref().is_some();
784 let path_contains_env = self.state.config.paths.module_search_paths.iter().any(|s| {
785 Some(s.as_str()) == rustpythonpath_env.as_deref() || Some(s.as_str()) == pythonpath_env.as_deref()
786 });
787
788 let guide_message = if cfg!(feature = "freeze-stdlib") {
789 "`rustpython_pylib` may not be set while using `freeze-stdlib` feature. Try using `rustpython::InterpreterBuilder::init_stdlib` or manually call `builder.add_frozen_modules(rustpython_pylib::FROZEN_STDLIB)` in `rustpython_vm::Interpreter::builder()`."
790 } else if !env_set {
791 "Neither RUSTPYTHONPATH nor PYTHONPATH is set. Try setting one of them to the stdlib directory."
792 } else if path_contains_env {
793 "RUSTPYTHONPATH or PYTHONPATH is set, but it doesn't contain the encodings library. If you are customizing the RustPython vm/interpreter, try adding the stdlib directory to the path. If you are developing the RustPython interpreter, it might be a bug during development."
794 } else {
795 "RUSTPYTHONPATH or PYTHONPATH is set, but it wasn't loaded to `PyConfig::paths::module_search_paths`. If you are going to customize the RustPython vm/interpreter, those environment variables are not loaded in the Settings struct by default. Please try creating a customized instance of the Settings struct. If you are developing the RustPython interpreter, it might be a bug during development."
796 };
797
798 let mut msg = format!(
799 "RustPython could not import the encodings module. It usually means something went wrong. Please carefully read the following messages and follow the steps.\n\
800 \n\
801 {guide_message}");
802 if !cfg!(feature = "freeze-stdlib") {
803 msg += "\n\
804 If you don't have access to a consistent external environment (e.g. targeting wasm, embedding \
805 rustpython in another application), try enabling the `freeze-stdlib` feature.\n\
806 If this is intended and you want to exclude the encodings module from your interpreter, please remove the `encodings` feature from `rustpython-vm` crate.";
807 }
808
809 let err = self.new_runtime_error(msg);
810 err.set___cause__(Some(import_err));
811 err
812 })?;
813 Ok(())
814 }
815
816 fn import_ascii_utf8_encodings(&mut self) -> PyResult<()> {
817 self.import("codecs", 0)?;
820
821 let (ascii_module_name, utf8_module_name) = if cfg!(feature = "freeze-stdlib") {
824 ("encodings.ascii", "encodings.utf_8")
825 } else {
826 ("encodings_ascii", "encodings_utf_8")
827 };
828
829 self.import(ascii_module_name, 0)?;
833 let sys_modules = self.sys_module.get_attr(identifier!(self, modules), self)?;
834 let ascii_module = sys_modules.get_item(ascii_module_name, self)?;
835 let getregentry = ascii_module.get_attr("getregentry", self)?;
836 let codec_info = getregentry.call((), self)?;
837 self.state
838 .codec_registry
839 .register_manual("ascii", codec_info.try_into_value(self)?)?;
840
841 self.import(utf8_module_name, 0)?;
844 let utf8_module = sys_modules.get_item(utf8_module_name, self)?;
845 let getregentry = utf8_module.get_attr("getregentry", self)?;
846 let codec_info = getregentry.call((), self)?;
847 let utf8_codec: crate::codecs::PyCodec = codec_info.try_into_value(self)?;
848 self.state
849 .codec_registry
850 .register_manual("utf-8", utf8_codec.clone())?;
851 self.state
852 .codec_registry
853 .register_manual("utf8", utf8_codec)?;
854
855 if cfg!(feature = "freeze-stdlib") {
858 self.import("encodings.latin_1", 0)?;
859 let latin1_module = sys_modules.get_item("encodings.latin_1", self)?;
860 let getregentry = latin1_module.get_attr("getregentry", self)?;
861 let codec_info = getregentry.call((), self)?;
862 let latin1_codec: crate::codecs::PyCodec = codec_info.try_into_value(self)?;
863 for name in ["latin-1", "latin_1", "latin1", "iso8859-1", "iso8859_1"] {
864 self.state
865 .codec_registry
866 .register_manual(name, latin1_codec.clone())?;
867 }
868 }
869 Ok(())
870 }
871
872 fn initialize(&mut self) {
873 flame_guard!("init VirtualMachine");
874
875 if self.initialized {
876 panic!("Double Initialize Error");
877 }
878
879 #[cfg(feature = "threading")]
881 stdlib::_thread::init_main_thread_ident(self);
882
883 stdlib::builtins::init_module(self, &self.builtins);
884 let callable_cache_init = self.init_callable_cache();
885 self.expect_pyresult(callable_cache_init, "failed to initialize callable cache");
886 stdlib::sys::init_module(self, &self.sys_module, &self.builtins);
887 self.expect_pyresult(
888 stdlib::sys::set_bootstrap_stderr(self),
889 "failed to initialize bootstrap stderr",
890 );
891
892 let mut essential_init = || -> PyResult {
893 import::import_builtin(self, "_typing")?;
894 #[cfg(all(not(target_arch = "wasm32"), feature = "host_env"))]
895 import::import_builtin(self, "_signal")?;
896 #[cfg(any(feature = "parser", feature = "compiler"))]
897 import::import_builtin(self, "_ast")?;
898 #[cfg(not(feature = "threading"))]
899 import::import_frozen(self, "_thread")?;
900 let importlib = import::init_importlib_base(self)?;
901 self.import_ascii_utf8_encodings()?;
902
903 {
904 let io = import::import_builtin(self, "_io")?;
905
906 #[cfg(all(feature = "host_env", feature = "stdio"))]
908 let make_stdio = |name: &str, fd: i32, write: bool| -> PyResult<PyObjectRef> {
909 let buffered_stdio = self.state.config.settings.buffered_stdio;
910 let unbuffered = write && !buffered_stdio;
911 let buf = crate::stdlib::_io::open(
912 self.ctx.new_int(fd).into(),
913 Some(if write { "wb" } else { "rb" }),
914 crate::stdlib::_io::OpenArgs {
915 buffering: if unbuffered { 0 } else { -1 },
916 closefd: false,
917 ..Default::default()
918 },
919 self,
920 )?;
921 let raw = if unbuffered {
922 buf.clone()
923 } else {
924 buf.get_attr("raw", self)?
925 };
926 raw.set_attr("name", self.ctx.new_str(format!("<{name}>")), self)?;
927 let isatty = self.call_method(&raw, "isatty", ())?.is_true(self)?;
928 let write_through = !buffered_stdio;
929 let line_buffering = buffered_stdio && (isatty || fd == 2);
930
931 let newline = if cfg!(windows) { None } else { Some("\n") };
932 let encoding = self.state.config.settings.stdio_encoding.as_deref();
933 let errors = if fd == 2 {
935 Some("backslashreplace")
936 } else {
937 self.state.config.settings.stdio_errors.as_deref().or(
938 if self.state.config.settings.stdio_encoding.is_some() {
939 Some("strict")
940 } else {
941 Some("surrogateescape")
942 },
943 )
944 };
945
946 let stdio = self.call_method(
947 &io,
948 "TextIOWrapper",
949 (
950 buf,
951 encoding,
952 errors,
953 newline,
954 line_buffering,
955 write_through,
956 ),
957 )?;
958 let mode = if write { "w" } else { "r" };
959 stdio.set_attr("mode", self.ctx.new_str(mode), self)?;
960 Ok::<_, self::PyBaseExceptionRef>(stdio)
961 };
962
963 #[cfg(all(not(feature = "host_env"), feature = "stdio"))]
965 let make_stdio = |name: &str, fd: i32, write: bool| {
966 let mode = if write { "w" } else { "r" };
967 let stdio = stdlib::sys::SandboxStdio {
968 fd,
969 name: format!("<{name}>"),
970 mode: mode.to_owned(),
971 }
972 .into_ref(&self.ctx);
973 Ok(stdio.into())
974 };
975
976 #[cfg(not(feature = "stdio"))]
978 let make_stdio = |_name: &str, _fd: i32, _write: bool| {
979 Ok(crate::builtins::PyNone.into_pyobject(self))
980 };
981
982 let set_stdio = |name, fd, write| {
983 let stdio: PyObjectRef = make_stdio(name, fd, write)?;
984 let dunder_name = self.ctx.intern_str(format!("__{name}__"));
985 self.sys_module.set_attr(
986 dunder_name, stdio.clone(),
988 self,
989 )?;
990 self.sys_module.set_attr(name, stdio, self)?;
991 Ok(())
992 };
993 set_stdio("stdin", 0, false)?;
994 set_stdio("stdout", 1, true)?;
995 set_stdio("stderr", 2, true)?;
996
997 let io_open = io.get_attr("open", self)?;
998 self.builtins.set_attr("open", io_open, self)?;
999 }
1000
1001 Ok(importlib)
1002 };
1003
1004 let res = essential_init();
1005 let importlib = self.expect_pyresult(res, "essential initialization failed");
1006
1007 #[cfg(feature = "host_env")]
1008 if self.state.config.settings.allow_external_library
1009 && cfg!(feature = "rustpython-compiler")
1010 && let Err(e) = import::init_importlib_package(self, importlib)
1011 {
1012 eprintln!(
1013 "importlib initialization failed. This is critical for many complicated packages."
1014 );
1015 self.print_exception(e);
1016 }
1017
1018 #[cfg(not(feature = "host_env"))]
1019 let _ = importlib;
1020
1021 let _expect_stdlib = cfg!(feature = "freeze-stdlib")
1022 || !self.state.config.paths.module_search_paths.is_empty();
1023
1024 #[cfg(feature = "encodings")]
1025 if _expect_stdlib {
1026 if let Err(e) = self.import_encodings() {
1027 eprintln!(
1028 "encodings initialization failed. Only utf-8 encoding will be supported."
1029 );
1030 self.print_exception(e);
1031 }
1032 } else {
1033 eprintln!(
1036 "feature `encodings` is enabled but `paths.module_search_paths` is empty. \
1037 Please add the library path to `settings.path_list`. If you intended to disable the entire standard library (including the `encodings` feature), please also make sure to disable the `encodings` feature.\n\
1038 Tip: You may also want to add `\"\"` to `settings.path_list` in order to enable importing from the current working directory."
1039 );
1040 }
1041
1042 self.initialized = true;
1043 }
1044
1045 pub fn set_user_signal_channel(&mut self, signal_rx: signal::UserSignalReceiver) {
1047 self.signal_rx = Some(signal_rx);
1048 }
1049
1050 pub fn run_pyc_bytes(&self, pyc_bytes: &[u8], scope: Scope) -> PyResult<()> {
1079 let code = PyCode::from_pyc(pyc_bytes, Some("<pyc_bytes>"), None, None, self)?;
1080 self.with_simple_run("<source>", |_module_dict| {
1081 self.run_code_obj(code, scope)?;
1082 Ok(())
1083 })
1084 }
1085
1086 pub fn run_code_obj(&self, code: PyRef<PyCode>, scope: Scope) -> PyResult {
1087 use crate::builtins::{PyFunction, PyModule};
1088
1089 let func = PyFunction::new(code.clone(), scope.globals.clone(), self)?;
1091 let func_obj = func.into_ref(&self.ctx).into();
1092
1093 let builtins = match scope
1095 .globals
1096 .get_item_opt(identifier!(self, __builtins__), self)?
1097 {
1098 Some(b) => {
1099 if let Some(module) = b.downcast_ref::<PyModule>() {
1100 module.dict().into()
1101 } else {
1102 b
1103 }
1104 }
1105 None => self.builtins.dict().into(),
1106 };
1107
1108 let frame =
1109 Frame::new(code, scope, builtins, &[], Some(func_obj), false, self).into_ref(&self.ctx);
1110 self.run_frame(frame)
1111 }
1112
1113 #[cold]
1114 pub fn run_unraisable(&self, e: PyBaseExceptionRef, msg: Option<String>, object: PyObjectRef) {
1115 if self.state.finalizing.load(Ordering::Acquire) {
1119 self.write_unraisable_to_stderr(&e, msg.as_deref(), &object);
1120 return;
1121 }
1122
1123 let sys_module = self.import("sys", 0).unwrap();
1124 let unraisablehook = sys_module.get_attr("unraisablehook", self).unwrap();
1125
1126 let exc_type = e.class().to_owned();
1127 let exc_traceback = e.__traceback__().to_pyobject(self); let exc_value = e.into();
1129 let args = stdlib::sys::UnraisableHookArgsData {
1130 exc_type,
1131 exc_value,
1132 exc_traceback,
1133 err_msg: self.new_pyobj(msg),
1134 object,
1135 };
1136 if let Err(e) = unraisablehook.call((args,), self) {
1137 println!("{}", e.as_object().repr(self).unwrap());
1138 }
1139 }
1140
1141 fn write_unraisable_to_stderr(
1144 &self,
1145 e: &PyBaseExceptionRef,
1146 msg: Option<&str>,
1147 object: &PyObjectRef,
1148 ) {
1149 let stderr = crate::stdlib::sys::get_stderr(self).ok();
1151
1152 let write_to_stderr = |s: &str, stderr: &Option<PyObjectRef>, vm: &VirtualMachine| {
1153 if let Some(stderr) = stderr {
1154 let _ = vm.call_method(stderr, "write", (s.to_owned(),));
1155 } else {
1156 eprint!("{}", s);
1157 }
1158 };
1159
1160 let msg_str = if let Some(msg) = msg {
1161 format!("{msg}: ")
1162 } else {
1163 "Exception ignored in: ".to_owned()
1164 };
1165 write_to_stderr(&msg_str, &stderr, self);
1166
1167 let repr_result = object.repr(self);
1168 let repr_wtf8 = repr_result
1169 .as_ref()
1170 .map_or("<object repr failed>".as_ref(), |s| s.as_wtf8());
1171 write_to_stderr(&format!("{repr_wtf8}\n"), &stderr, self);
1172
1173 let exc_type_name = e.class().name();
1175 let msg = match e.as_object().str(self) {
1176 Ok(exc_str) if !exc_str.as_wtf8().is_empty() => {
1177 format!("{}: {}\n", exc_type_name, exc_str.as_wtf8())
1178 }
1179 _ => format!("{}\n", exc_type_name),
1180 };
1181 write_to_stderr(&msg, &stderr, self);
1182
1183 if let Some(ref stderr) = stderr {
1185 let _ = self.call_method(stderr, "flush", ());
1186 }
1187 }
1188
1189 #[inline(always)]
1190 pub fn run_frame(&self, frame: FrameRef) -> PyResult {
1191 match self.with_frame(frame, |f| f.run(self))? {
1192 ExecutionResult::Return(value) => Ok(value),
1193 _ => panic!("Got unexpected result from function"),
1194 }
1195 }
1196
1197 fn with_simple_run(
1199 &self,
1200 path: &str,
1201 run: impl FnOnce(&Py<PyDict>) -> PyResult<()>,
1202 ) -> PyResult<()> {
1203 let sys_modules = self.sys_module.get_attr(identifier!(self, modules), self)?;
1204 let main_module = sys_modules.get_item(identifier!(self, __main__), self)?;
1205 let module_dict = main_module.dict().expect("main module must have __dict__");
1206
1207 let set_file_name = !module_dict.contains_key(identifier!(self, __file__), self);
1209 if set_file_name {
1210 module_dict.set_item(
1211 identifier!(self, __file__),
1212 self.ctx.new_str(path).into(),
1213 self,
1214 )?;
1215 module_dict.set_item(identifier!(self, __cached__), self.ctx.none(), self)?;
1216 }
1217
1218 let result = run(&module_dict);
1219
1220 self.flush_io();
1221
1222 if set_file_name {
1224 let _ = module_dict.del_item(identifier!(self, __file__), self);
1225 let _ = module_dict.del_item(identifier!(self, __cached__), self);
1226 }
1227
1228 result
1229 }
1230
1231 fn flush_io(&self) {
1235 if let Ok(stdout) = self.sys_module.get_attr("stdout", self) {
1236 let _ = self.call_method(&stdout, identifier!(self, flush).as_str(), ());
1237 }
1238 if let Ok(stderr) = self.sys_module.get_attr("stderr", self) {
1239 let _ = self.call_method(&stderr, identifier!(self, flush).as_str(), ());
1240 }
1241 }
1242
1243 pub fn finalize_modules(&self) {
1247 self.finalize_modules_delete_special();
1249
1250 let module_weakrefs = self.finalize_remove_modules();
1254
1255 self.finalize_clear_modules_dict();
1257
1258 crate::gc_state::gc_state().collect_force(2);
1262
1263 self.finalize_clear_module_dicts(&module_weakrefs);
1266
1267 crate::gc_state::gc_state().collect_force(2);
1269
1270 self.finalize_clear_sys_builtins_dict();
1272 }
1273
1274 fn finalize_modules_delete_special(&self) {
1276 let none = self.ctx.none();
1277 let sys_dict = self.sys_module.dict();
1278
1279 for attr in &[
1281 "path",
1282 "argv",
1283 "ps1",
1284 "ps2",
1285 "last_exc",
1286 "last_type",
1287 "last_value",
1288 "last_traceback",
1289 "path_importer_cache",
1290 "meta_path",
1291 "path_hooks",
1292 ] {
1293 let _ = sys_dict.set_item(*attr, none.clone(), self);
1294 }
1295
1296 for (std_name, dunder_name) in &[
1298 ("stdin", "__stdin__"),
1299 ("stdout", "__stdout__"),
1300 ("stderr", "__stderr__"),
1301 ] {
1302 let restored = sys_dict
1303 .get_item_opt(*dunder_name, self)
1304 .ok()
1305 .flatten()
1306 .unwrap_or_else(|| none.clone());
1307 let _ = sys_dict.set_item(*std_name, restored, self);
1308 }
1309
1310 let _ = self.builtins.dict().set_item("_", none, self);
1312 }
1313
1314 fn finalize_remove_modules(&self) -> Vec<(String, PyRef<PyWeak>)> {
1318 let mut module_weakrefs = Vec::new();
1319
1320 let Ok(modules) = self.sys_module.get_attr(identifier!(self, modules), self) else {
1321 return module_weakrefs;
1322 };
1323 let Some(modules_dict) = modules.downcast_ref::<PyDict>() else {
1324 return module_weakrefs;
1325 };
1326
1327 let none = self.ctx.none();
1328 let items: Vec<_> = modules_dict.into_iter().collect();
1329
1330 for (key, value) in items {
1331 let name = key
1332 .downcast_ref::<PyUtf8Str>()
1333 .map(|s| s.as_str().to_owned())
1334 .unwrap_or_default();
1335
1336 if value.downcast_ref::<PyModule>().is_some()
1338 && let Ok(weak) = value.downgrade(None, self)
1339 {
1340 module_weakrefs.push((name, weak));
1341 }
1342
1343 let _ = modules_dict.set_item(&*key, none.clone(), self);
1345 }
1346
1347 module_weakrefs
1348 }
1349
1350 fn finalize_clear_modules_dict(&self) {
1352 if let Ok(modules) = self.sys_module.get_attr(identifier!(self, modules), self)
1353 && let Some(modules_dict) = modules.downcast_ref::<PyDict>()
1354 {
1355 modules_dict.clear();
1356 }
1357 }
1358
1359 fn finalize_clear_module_dicts(&self, module_weakrefs: &[(String, PyRef<PyWeak>)]) {
1362 let builtins_dict = self.builtins.dict();
1363 let sys_dict = self.sys_module.dict();
1364
1365 for (_name, weakref) in module_weakrefs.iter().rev() {
1366 let Some(module_obj) = weakref.upgrade() else {
1367 continue;
1368 };
1369 let Some(module) = module_obj.downcast_ref::<PyModule>() else {
1370 continue;
1371 };
1372
1373 let dict = module.dict();
1374 if dict.is(&builtins_dict) || dict.is(&sys_dict) {
1376 continue;
1377 }
1378
1379 Self::module_clear_dict(&dict, self);
1380 }
1381 }
1382
1383 pub(crate) fn module_clear_dict(dict: &Py<PyDict>, vm: &VirtualMachine) {
1387 let none = vm.ctx.none();
1388
1389 for (key, value) in dict.into_iter().collect::<Vec<_>>() {
1391 if vm.is_none(&value) {
1392 continue;
1393 }
1394 if let Some(key_str) = key.downcast_ref::<PyStr>() {
1395 let name = key_str.as_wtf8();
1396 if name.starts_with("_") && name != "__builtins__" {
1397 let _ = dict.set_item(key_str, none.clone(), vm);
1398 }
1399 }
1400 }
1401
1402 for (key, value) in dict.into_iter().collect::<Vec<_>>() {
1404 if vm.is_none(&value) {
1405 continue;
1406 }
1407 if let Some(key_str) = key.downcast_ref::<PyStr>()
1408 && key_str.as_bytes() != b"__builtins__"
1409 {
1410 let _ = dict.set_item(key_str.as_wtf8(), none.clone(), vm);
1411 }
1412 }
1413 }
1414
1415 fn finalize_clear_sys_builtins_dict(&self) {
1417 Self::module_clear_dict(&self.sys_module.dict(), self);
1418 Self::module_clear_dict(&self.builtins.dict(), self);
1419 }
1420
1421 pub fn current_recursion_depth(&self) -> usize {
1422 self.recursion_depth.get()
1423 }
1424
1425 #[cfg_attr(miri, allow(dead_code))]
1428 const STACK_MARGIN_BYTES: usize = 2048 * core::mem::size_of::<usize>();
1429
1430 #[cfg(all(not(miri), windows))]
1433 fn get_stack_bounds() -> (usize, usize) {
1434 use windows_sys::Win32::System::Threading::{
1435 GetCurrentThreadStackLimits, SetThreadStackGuarantee,
1436 };
1437 let mut low: usize = 0;
1438 let mut high: usize = 0;
1439 unsafe {
1440 GetCurrentThreadStackLimits(&mut low as *mut usize, &mut high as *mut usize);
1441 let mut guarantee: u32 = 0;
1443 SetThreadStackGuarantee(&mut guarantee);
1444 low += guarantee as usize;
1445 }
1446 (low, high)
1447 }
1448
1449 #[cfg(all(not(miri), not(windows)))]
1452 fn get_stack_bounds() -> (usize, usize) {
1453 #[cfg(any(target_os = "linux", target_os = "android"))]
1455 {
1456 use libc::{
1457 pthread_attr_destroy, pthread_attr_getstack, pthread_attr_t, pthread_getattr_np,
1458 pthread_self,
1459 };
1460 let mut attr: pthread_attr_t = unsafe { core::mem::zeroed() };
1461 unsafe {
1462 if pthread_getattr_np(pthread_self(), &mut attr) == 0 {
1463 let mut stack_addr: *mut libc::c_void = core::ptr::null_mut();
1464 let mut stack_size: libc::size_t = 0;
1465 if pthread_attr_getstack(&attr, &mut stack_addr, &mut stack_size) == 0 {
1466 pthread_attr_destroy(&mut attr);
1467 let base = stack_addr as usize;
1468 let top = base + stack_size;
1469 return (base, top);
1470 }
1471 pthread_attr_destroy(&mut attr);
1472 }
1473 }
1474 }
1475
1476 #[cfg(target_os = "macos")]
1477 {
1478 use libc::{pthread_get_stackaddr_np, pthread_get_stacksize_np, pthread_self};
1479 unsafe {
1480 let thread = pthread_self();
1481 let stack_top = pthread_get_stackaddr_np(thread) as usize;
1482 let stack_size = pthread_get_stacksize_np(thread);
1483 let stack_base = stack_top - stack_size;
1484 return (stack_base, stack_top);
1485 }
1486 }
1487
1488 #[allow(unreachable_code)]
1490 {
1491 let current_sp = psm::stack_pointer() as usize;
1492 let estimated_size = 8 * 1024 * 1024;
1494 let base = current_sp.saturating_sub(estimated_size);
1495 let top = current_sp + 1024 * 1024; (base, top)
1497 }
1498 }
1499
1500 #[cfg(not(miri))]
1503 fn calculate_c_stack_soft_limit() -> usize {
1504 let (base, _top) = Self::get_stack_bounds();
1505 base + Self::STACK_MARGIN_BYTES * 2
1507 }
1508
1509 #[cfg(miri)]
1511 fn calculate_c_stack_soft_limit() -> usize {
1512 0
1513 }
1514
1515 #[cfg(not(miri))]
1519 #[inline(always)]
1520 fn check_c_stack_overflow(&self) -> bool {
1521 let current_sp = psm::stack_pointer() as usize;
1522 let soft_limit = self.c_stack_soft_limit.get();
1523 current_sp < soft_limit
1526 && current_sp >= soft_limit.saturating_sub(Self::STACK_MARGIN_BYTES * 2)
1527 }
1528
1529 #[cfg(miri)]
1531 #[inline(always)]
1532 fn check_c_stack_overflow(&self) -> bool {
1533 false
1534 }
1535
1536 pub fn with_recursion<R, F: FnOnce() -> PyResult<R>>(&self, _where: &str, f: F) -> PyResult<R> {
1540 self.check_recursive_call(_where)?;
1541
1542 if self.check_c_stack_overflow() {
1544 return Err(self.new_recursion_error(_where.to_string()));
1545 }
1546
1547 self.recursion_depth.update(|d| d + 1);
1548 scopeguard::defer! { self.recursion_depth.update(|d| d - 1) }
1549 f()
1550 }
1551
1552 pub fn with_frame<R, F: FnOnce(FrameRef) -> PyResult<R>>(
1553 &self,
1554 frame: FrameRef,
1555 f: F,
1556 ) -> PyResult<R> {
1557 self.with_frame_impl(frame, None, true, f)
1558 }
1559
1560 pub fn with_frame_exc<R, F: FnOnce(FrameRef) -> PyResult<R>>(
1562 &self,
1563 frame: FrameRef,
1564 exc: Option<PyBaseExceptionRef>,
1565 f: F,
1566 ) -> PyResult<R> {
1567 self.with_frame_impl(frame, exc, true, f)
1568 }
1569
1570 pub(crate) fn with_frame_untraced<R, F: FnOnce(FrameRef) -> PyResult<R>>(
1571 &self,
1572 frame: FrameRef,
1573 f: F,
1574 ) -> PyResult<R> {
1575 self.with_frame_impl(frame, None, false, f)
1576 }
1577
1578 fn with_frame_impl<R, F: FnOnce(FrameRef) -> PyResult<R>>(
1579 &self,
1580 frame: FrameRef,
1581 exc: Option<PyBaseExceptionRef>,
1582 traced: bool,
1583 f: F,
1584 ) -> PyResult<R> {
1585 self.with_recursion("", || {
1586 let fp = FramePtr(NonNull::from(&*frame));
1590 self.frames.borrow_mut().push(fp);
1591 #[cfg(feature = "threading")]
1593 crate::vm::thread::push_thread_frame(fp);
1594 let old_frame = crate::vm::thread::set_current_frame((&**frame) as *const Frame);
1596 frame.previous.store(
1597 old_frame as *mut Frame,
1598 core::sync::atomic::Ordering::Relaxed,
1599 );
1600 self.push_exception(exc);
1604 let old_owner = frame.owner.swap(
1605 crate::frame::FrameOwner::Thread as i8,
1606 core::sync::atomic::Ordering::AcqRel,
1607 );
1608
1609 scopeguard::defer! {
1611 frame.owner.store(old_owner, core::sync::atomic::Ordering::Release);
1612 self.pop_exception();
1613 crate::vm::thread::set_current_frame(old_frame);
1614 self.frames.borrow_mut().pop();
1615 #[cfg(feature = "threading")]
1616 crate::vm::thread::pop_thread_frame();
1617 }
1618
1619 if traced {
1620 self.dispatch_traced_frame(&frame, |frame| f(frame.to_owned()))
1621 } else {
1622 f(frame.to_owned())
1623 }
1624 })
1625 }
1626
1627 pub fn resume_gen_frame<R, F: FnOnce(&Py<Frame>) -> PyResult<R>>(
1631 &self,
1632 frame: &FrameRef,
1633 exc: Option<PyBaseExceptionRef>,
1634 f: F,
1635 ) -> PyResult<R> {
1636 self.check_recursive_call("")?;
1637 if self.check_c_stack_overflow() {
1638 return Err(self.new_recursion_error(String::new()));
1639 }
1640 self.recursion_depth.update(|d| d + 1);
1641
1642 let fp = FramePtr(NonNull::from(&**frame));
1644 self.frames.borrow_mut().push(fp);
1645 #[cfg(feature = "threading")]
1646 crate::vm::thread::push_thread_frame(fp);
1647 let old_frame = crate::vm::thread::set_current_frame((&***frame) as *const Frame);
1648 frame.previous.store(
1649 old_frame as *mut Frame,
1650 core::sync::atomic::Ordering::Relaxed,
1651 );
1652 self.exceptions.borrow_mut().stack.push(exc);
1654 let old_owner = frame.owner.swap(
1655 crate::frame::FrameOwner::Thread as i8,
1656 core::sync::atomic::Ordering::AcqRel,
1657 );
1658
1659 scopeguard::defer! {
1662 frame.owner.store(old_owner, core::sync::atomic::Ordering::Release);
1663 self.exceptions.borrow_mut().stack
1664 .pop()
1665 .expect("pop_exception() without nested exc stack");
1666 crate::vm::thread::set_current_frame(old_frame);
1667 self.frames.borrow_mut().pop();
1668 #[cfg(feature = "threading")]
1669 crate::vm::thread::pop_thread_frame();
1670
1671 self.recursion_depth.update(|d| d - 1);
1672 }
1673
1674 self.dispatch_traced_frame(frame, |frame| f(frame))
1675 }
1676
1677 fn dispatch_traced_frame<R, F: FnOnce(&Py<Frame>) -> PyResult<R>>(
1687 &self,
1688 frame: &Py<Frame>,
1689 f: F,
1690 ) -> PyResult<R> {
1691 use crate::protocol::TraceEvent;
1692
1693 let trace_result = self.trace_event(TraceEvent::Call, None)?;
1695 if let Some(local_trace) = trace_result {
1696 *frame.trace.lock() = local_trace;
1697 }
1698
1699 let result = f(frame);
1700
1701 if self.use_tracing.get()
1705 && (!self.is_none(&frame.trace.lock()) || !self.is_none(&self.profile_func.borrow()))
1706 {
1707 let ret_result = self.trace_event(TraceEvent::Return, None);
1708 ret_result?;
1711 }
1712
1713 result
1714 }
1715
1716 #[cfg(feature = "rustpython-codegen")]
1719 pub fn compile_opts(&self) -> crate::compiler::CompileOpts {
1720 crate::compiler::CompileOpts {
1721 optimize: self.state.config.settings.optimize,
1722 debug_ranges: self.state.config.settings.code_debug_ranges,
1723 }
1724 }
1725
1726 fn check_recursive_call(&self, _where: &str) -> PyResult<()> {
1728 if self.recursion_depth.get() >= self.recursion_limit.get() {
1729 Err(self.new_recursion_error(format!("maximum recursion depth exceeded {_where}")))
1730 } else {
1731 Ok(())
1732 }
1733 }
1734
1735 pub fn current_frame(&self) -> Option<FrameRef> {
1736 self.frames.borrow().last().map(|fp| {
1737 unsafe { fp.as_ref() }.to_owned()
1739 })
1740 }
1741
1742 pub fn current_locals(&self) -> PyResult<ArgMapping> {
1743 self.current_frame()
1744 .expect("called current_locals but no frames on the stack")
1745 .locals(self)
1746 }
1747
1748 pub fn current_globals(&self) -> PyDictRef {
1749 self.current_frame()
1750 .expect("called current_globals but no frames on the stack")
1751 .globals
1752 .clone()
1753 }
1754
1755 pub fn try_class(&self, module: &'static str, class: &'static str) -> PyResult<PyTypeRef> {
1756 let class = self
1757 .import(module, 0)?
1758 .get_attr(class, self)?
1759 .downcast()
1760 .expect("not a class");
1761 Ok(class)
1762 }
1763
1764 pub fn class(&self, module: &'static str, class: &'static str) -> PyTypeRef {
1765 let module = self
1766 .import(module, 0)
1767 .unwrap_or_else(|_| panic!("unable to import {module}"));
1768
1769 let class = module
1770 .get_attr(class, self)
1771 .unwrap_or_else(|_| panic!("module {module:?} has no class {class}"));
1772 class.downcast().expect("not a class")
1773 }
1774
1775 #[inline]
1781 pub fn import<'a>(&self, module_name: impl AsPyStr<'a>, level: usize) -> PyResult {
1782 let module_name = module_name.as_pystr(&self.ctx);
1783 let from_list = self.ctx.empty_tuple_typed();
1784 self.import_inner(module_name, from_list, level)
1785 }
1786
1787 #[inline]
1790 pub fn import_from<'a>(
1791 &self,
1792 module_name: impl AsPyStr<'a>,
1793 from_list: &Py<PyTuple<PyStrRef>>,
1794 level: usize,
1795 ) -> PyResult {
1796 let module_name = module_name.as_pystr(&self.ctx);
1797 self.import_inner(module_name, from_list, level)
1798 }
1799
1800 fn import_inner(
1801 &self,
1802 module: &Py<PyStr>,
1803 from_list: &Py<PyTuple<PyStrRef>>,
1804 level: usize,
1805 ) -> PyResult {
1806 let import_func = self
1807 .builtins
1808 .get_attr(identifier!(self, __import__), self)
1809 .map_err(|_| self.new_import_error("__import__ not found", module.to_owned()))?;
1810
1811 let (locals, globals) = if let Some(frame) = self.current_frame() {
1812 (
1813 Some(frame.locals.clone_mapping(self)),
1814 Some(frame.globals.clone()),
1815 )
1816 } else {
1817 (None, None)
1818 };
1819 let from_list: PyObjectRef = from_list.to_owned().into();
1820 import_func
1821 .call((module.to_owned(), globals, locals, from_list, level), self)
1822 .inspect_err(|exc| import::remove_importlib_frames(self, exc))
1823 }
1824
1825 pub fn extract_elements_with<T, F>(&self, value: &PyObject, func: F) -> PyResult<Vec<T>>
1826 where
1827 F: Fn(PyObjectRef) -> PyResult<T>,
1828 {
1829 let cls = value.class();
1833 let list_borrow;
1834 let slice = if cls.is(self.ctx.types.tuple_type) {
1835 value.downcast_ref::<PyTuple>().unwrap().as_slice()
1836 } else if cls.is(self.ctx.types.list_type) {
1837 list_borrow = value.downcast_ref::<PyList>().unwrap().borrow_vec();
1838 &list_borrow
1839 } else if cls.is(self.ctx.types.dict_type) {
1840 let keys = value.downcast_ref::<PyDict>().unwrap().keys_vec();
1841 return keys.into_iter().map(func).collect();
1842 } else if cls.is(self.ctx.types.dict_keys_type) {
1843 let keys = value.downcast_ref::<PyDictKeys>().unwrap().dict.keys_vec();
1844 return keys.into_iter().map(func).collect();
1845 } else if cls.is(self.ctx.types.dict_values_type) {
1846 let values = value
1847 .downcast_ref::<PyDictValues>()
1848 .unwrap()
1849 .dict
1850 .values_vec();
1851 return values.into_iter().map(func).collect();
1852 } else if cls.is(self.ctx.types.dict_items_type) {
1853 let items = value
1854 .downcast_ref::<PyDictItems>()
1855 .unwrap()
1856 .dict
1857 .items_vec();
1858 return items
1859 .into_iter()
1860 .map(|(k, v)| func(self.ctx.new_tuple(vec![k, v]).into()))
1861 .collect();
1862 } else {
1863 return self.map_py_iter(value, func);
1864 };
1865 slice.iter().map(|obj| func(obj.clone())).collect()
1866 }
1867
1868 pub fn map_iterable_object<F, R>(&self, obj: &PyObject, mut f: F) -> PyResult<PyResult<Vec<R>>>
1869 where
1870 F: FnMut(PyObjectRef) -> PyResult<R>,
1871 {
1872 match_class!(match obj {
1873 ref l @ PyList => {
1874 let mut i: usize = 0;
1875 let mut results = Vec::with_capacity(l.borrow_vec().len());
1876 loop {
1877 let elem = {
1878 let elements = &*l.borrow_vec();
1879 if i >= elements.len() {
1880 results.shrink_to_fit();
1881 return Ok(Ok(results));
1882 } else {
1883 elements[i].clone()
1884 }
1885 };
1887 match f(elem) {
1888 Ok(result) => results.push(result),
1889 Err(err) => return Ok(Err(err)),
1890 }
1891 i += 1;
1892 }
1893 }
1894 ref t @ PyTuple => Ok(t.iter().cloned().map(f).collect()),
1895 obj => {
1897 Ok(self.map_py_iter(obj, f))
1898 }
1899 })
1900 }
1901
1902 fn map_py_iter<F, R>(&self, value: &PyObject, mut f: F) -> PyResult<Vec<R>>
1903 where
1904 F: FnMut(PyObjectRef) -> PyResult<R>,
1905 {
1906 let iter = value.to_owned().get_iter(self)?;
1907 let cap = match self.length_hint_opt(value.to_owned()) {
1908 Err(e) if e.class().is(self.ctx.exceptions.runtime_error) => return Err(e),
1909 Ok(Some(value)) => Some(value),
1910 _ => None,
1912 };
1913 if let Some(cap) = cap
1916 && cap >= isize::MAX as usize
1917 {
1918 return Ok(Vec::new());
1919 }
1920
1921 let mut results = PyIterIter::new(self, iter.as_ref(), cap)
1922 .map(|element| f(element?))
1923 .collect::<PyResult<Vec<_>>>()?;
1924 results.shrink_to_fit();
1925 Ok(results)
1926 }
1927
1928 pub fn get_attribute_opt<'a>(
1929 &self,
1930 obj: PyObjectRef,
1931 attr_name: impl AsPyStr<'a>,
1932 ) -> PyResult<Option<PyObjectRef>> {
1933 let attr_name = attr_name.as_pystr(&self.ctx);
1934 match obj.get_attr_inner(attr_name, self) {
1935 Ok(attr) => Ok(Some(attr)),
1936 Err(e) if e.fast_isinstance(self.ctx.exceptions.attribute_error) => Ok(None),
1937 Err(e) => Err(e),
1938 }
1939 }
1940
1941 pub fn set_attribute_error_context(
1942 &self,
1943 exc: &Py<PyBaseException>,
1944 obj: PyObjectRef,
1945 name: PyStrRef,
1946 ) {
1947 if exc.class().is(self.ctx.exceptions.attribute_error) {
1948 let exc = exc.as_object();
1949 let already_set = exc
1951 .get_attr("name", self)
1952 .ok()
1953 .is_some_and(|v| !self.is_none(&v));
1954 if already_set {
1955 return;
1956 }
1957 exc.set_attr("name", name, self).unwrap();
1958 exc.set_attr("obj", obj, self).unwrap();
1959 }
1960 }
1961
1962 pub fn get_method_or_type_error<F>(
1965 &self,
1966 obj: PyObjectRef,
1967 method_name: &'static PyStrInterned,
1968 err_msg: F,
1969 ) -> PyResult
1970 where
1971 F: FnOnce() -> String,
1972 {
1973 let method = obj
1974 .class()
1975 .get_attr(method_name)
1976 .ok_or_else(|| self.new_type_error(err_msg()))?;
1977 self.call_if_get_descriptor(&method, obj)
1978 }
1979
1980 pub(crate) fn get_method(
1982 &self,
1983 obj: PyObjectRef,
1984 method_name: &'static PyStrInterned,
1985 ) -> Option<PyResult> {
1986 let method = obj.get_class_attr(method_name)?;
1987 Some(self.call_if_get_descriptor(&method, obj))
1988 }
1989
1990 pub(crate) fn get_str_method(&self, obj: PyObjectRef, method_name: &str) -> Option<PyResult> {
1991 let method_name = self.ctx.interned_str(method_name)?;
1992 self.get_method(obj, method_name)
1993 }
1994
1995 #[inline]
1996 pub(crate) fn eval_breaker_tripped(&self) -> bool {
1997 #[cfg(feature = "threading")]
1998 if self.state.finalizing.load(Ordering::Relaxed) && !self.is_main_thread() {
1999 return true;
2000 }
2001
2002 #[cfg(all(unix, feature = "threading"))]
2003 if thread::stop_requested_for_current_thread() {
2004 return true;
2005 }
2006
2007 #[cfg(not(target_arch = "wasm32"))]
2008 if crate::signal::is_triggered() {
2009 return true;
2010 }
2011
2012 false
2013 }
2014
2015 #[inline]
2016 pub fn check_signals(&self) -> PyResult<()> {
2019 #[cfg(feature = "threading")]
2020 if self.state.finalizing.load(Ordering::Acquire) && !self.is_main_thread() {
2021 return Err(self.new_exception(self.ctx.exceptions.system_exit.to_owned(), vec![]));
2024 }
2025
2026 #[cfg(all(unix, feature = "threading"))]
2028 thread::suspend_if_needed(&self.state.stop_the_world);
2029
2030 #[cfg(not(target_arch = "wasm32"))]
2031 {
2032 crate::signal::check_signals(self)
2033 }
2034 #[cfg(target_arch = "wasm32")]
2035 {
2036 Ok(())
2037 }
2038 }
2039
2040 pub(crate) fn push_exception(&self, exc: Option<PyBaseExceptionRef>) {
2041 self.exceptions.borrow_mut().stack.push(exc);
2042 #[cfg(feature = "threading")]
2043 thread::update_thread_exception(self.topmost_exception());
2044 }
2045
2046 pub(crate) fn pop_exception(&self) -> Option<PyBaseExceptionRef> {
2047 let exc = self
2048 .exceptions
2049 .borrow_mut()
2050 .stack
2051 .pop()
2052 .expect("pop_exception() without nested exc stack");
2053 #[cfg(feature = "threading")]
2054 thread::update_thread_exception(self.topmost_exception());
2055 exc
2056 }
2057
2058 pub(crate) fn current_exception(&self) -> Option<PyBaseExceptionRef> {
2059 self.exceptions.borrow().stack.last().cloned().flatten()
2060 }
2061
2062 pub(crate) fn set_exception(&self, exc: Option<PyBaseExceptionRef>) {
2063 let mut excs = self.exceptions.borrow_mut();
2065 debug_assert!(
2066 !excs.stack.is_empty(),
2067 "set_exception called with empty exception stack"
2068 );
2069 if let Some(top) = excs.stack.last_mut() {
2070 let prev = core::mem::replace(top, exc);
2071 drop(excs);
2072 drop(prev);
2073 } else {
2074 excs.stack.push(exc);
2075 drop(excs);
2076 }
2077 #[cfg(feature = "threading")]
2078 thread::update_thread_exception(self.topmost_exception());
2079 }
2080
2081 pub(crate) fn contextualize_exception(&self, exception: &Py<PyBaseException>) {
2082 if let Some(context_exc) = self.topmost_exception()
2083 && !context_exc.is(exception)
2084 {
2085 let mut o = context_exc.clone();
2088 let mut slow_o = context_exc.clone();
2089 let mut slow_update_toggle = false;
2090 while let Some(context) = o.__context__() {
2091 if context.is(exception) {
2092 o.set___context__(None);
2093 break;
2094 }
2095 o = context;
2096 if o.is(&slow_o) {
2097 break;
2099 }
2100 if slow_update_toggle && let Some(slow_context) = slow_o.__context__() {
2101 slow_o = slow_context;
2102 }
2103 slow_update_toggle = !slow_update_toggle;
2104 }
2105 exception.set___context__(Some(context_exc))
2106 }
2107 }
2108
2109 pub(crate) fn topmost_exception(&self) -> Option<PyBaseExceptionRef> {
2110 let excs = self.exceptions.borrow();
2111 excs.stack.iter().rev().find_map(|e| e.clone())
2112 }
2113
2114 pub fn handle_exit_exception(&self, exc: PyBaseExceptionRef) -> u32 {
2115 if exc.fast_isinstance(self.ctx.exceptions.system_exit) {
2116 let args = exc.args();
2117 let msg = match args.as_slice() {
2118 [] => return 0,
2119 [arg] => match_class!(match arg {
2120 ref i @ PyInt => {
2121 use num_traits::cast::ToPrimitive;
2122 let code = i
2124 .as_bigint()
2125 .to_u32()
2126 .or_else(|| i.as_bigint().to_i32().map(|v| v as u32))
2127 .unwrap_or(-1i32 as u32);
2128 return code;
2129 }
2130 arg => {
2131 if self.is_none(arg) {
2132 return 0;
2133 } else {
2134 arg.str(self).ok()
2135 }
2136 }
2137 }),
2138 _ => args.as_object().repr(self).ok(),
2139 };
2140 if let Some(msg) = msg {
2141 if let Ok(stderr) = stdlib::sys::get_stderr(self) {
2143 let _ = self.call_method(&stderr, "write", (msg,));
2144 let _ = self.call_method(&stderr, "write", ("\n",));
2145 }
2146 }
2147 1
2148 } else if exc.fast_isinstance(self.ctx.exceptions.keyboard_interrupt) {
2149 #[allow(clippy::if_same_then_else)]
2150 {
2151 self.print_exception(exc);
2152 #[cfg(unix)]
2153 {
2154 let action = SigAction::new(
2155 nix::sys::signal::SigHandler::SigDfl,
2156 SaFlags::SA_ONSTACK,
2157 SigSet::empty(),
2158 );
2159 let result = unsafe { sigaction(SIGINT, &action) };
2160 if result.is_ok() {
2161 self.flush_std();
2162 kill(getpid(), SIGINT).expect("Expect to be killed.");
2163 }
2164
2165 (libc::SIGINT as u32) + 128
2166 }
2167 #[cfg(windows)]
2168 {
2169 0xC000013A
2171 }
2172 #[cfg(not(any(unix, windows)))]
2173 {
2174 1
2175 }
2176 }
2177 } else {
2178 self.print_exception(exc);
2179 1
2180 }
2181 }
2182
2183 #[doc(hidden)]
2184 pub fn __module_set_attr(
2185 &self,
2186 module: &Py<PyModule>,
2187 attr_name: &'static PyStrInterned,
2188 attr_value: impl Into<PyObjectRef>,
2189 ) -> PyResult<()> {
2190 let val = attr_value.into();
2191 module
2192 .as_object()
2193 .generic_setattr(attr_name, PySetterValue::Assign(val), self)
2194 }
2195
2196 pub fn insert_sys_path(&self, obj: PyObjectRef) -> PyResult<()> {
2197 let sys_path = self.sys_module.get_attr("path", self).unwrap();
2198 self.call_method(&sys_path, "insert", (0, obj))?;
2199 Ok(())
2200 }
2201
2202 pub fn run_module(&self, module: &str) -> PyResult<()> {
2203 let runpy = self.import("runpy", 0)?;
2204 let run_module_as_main = runpy.get_attr("_run_module_as_main", self)?;
2205 run_module_as_main.call((module,), self)?;
2206 Ok(())
2207 }
2208
2209 pub fn fs_encoding(&self) -> &'static PyStrInterned {
2210 identifier!(self, utf_8)
2211 }
2212
2213 pub fn fs_encode_errors(&self) -> &'static PyUtf8StrInterned {
2214 if cfg!(windows) {
2215 identifier_utf8!(self, surrogatepass)
2216 } else {
2217 identifier_utf8!(self, surrogateescape)
2218 }
2219 }
2220
2221 pub fn fsdecode(&self, s: impl Into<OsString>) -> PyStrRef {
2222 match s.into().into_string() {
2223 Ok(s) => self.ctx.new_str(s),
2224 Err(s) => {
2225 let bytes = self.ctx.new_bytes(s.into_encoded_bytes());
2226 let errors = self.fs_encode_errors().to_owned();
2227 let res = self.state.codec_registry.decode_text(
2228 bytes.into(),
2229 "utf-8",
2230 Some(errors),
2231 self,
2232 );
2233 self.expect_pyresult(res, "fsdecode should be lossless and never fail")
2234 }
2235 }
2236 }
2237
2238 pub fn fsencode<'a>(&self, s: &'a Py<PyStr>) -> PyResult<Cow<'a, OsStr>> {
2239 if cfg!(windows) || s.is_utf8() {
2240 let s = unsafe { OsStr::from_encoded_bytes_unchecked(s.as_bytes()) };
2243 return Ok(Cow::Borrowed(s));
2244 }
2245 let errors = self.fs_encode_errors().to_owned();
2246 let bytes = self
2247 .state
2248 .codec_registry
2249 .encode_text(s.to_owned(), "utf-8", Some(errors), self)?
2250 .to_vec();
2251 let s = unsafe { OsString::from_encoded_bytes_unchecked(bytes) };
2254 Ok(Cow::Owned(s))
2255 }
2256}
2257
2258impl AsRef<Context> for VirtualMachine {
2259 fn as_ref(&self) -> &Context {
2260 &self.ctx
2261 }
2262}
2263
2264pub fn resolve_frozen_alias(name: &str) -> &str {
2267 match name {
2268 "_frozen_importlib" => "importlib._bootstrap",
2269 "_frozen_importlib_external" => "importlib._bootstrap_external",
2270 "encodings_ascii" => "encodings.ascii",
2271 "encodings_utf_8" => "encodings.utf_8",
2272 "__hello_alias__" | "__phello_alias__" | "__phello_alias__.spam" => "__hello__",
2273 "__phello__.__init__" => "<__phello__",
2274 "__phello__.ham.__init__" => "<__phello__.ham",
2275 "__hello_only__" => "",
2276 _ => name,
2277 }
2278}
2279
2280#[test]
2281fn test_nested_frozen() {
2282 use rustpython_vm as vm;
2283
2284 vm::Interpreter::builder(Default::default())
2285 .add_frozen_modules(rustpython_vm::py_freeze!(
2286 dir = "../../../../extra_tests/snippets"
2287 ))
2288 .build()
2289 .enter(|vm| {
2290 let scope = vm.new_scope_with_builtins();
2291
2292 let source = "from dir_module.dir_module_inner import value2";
2293 let code_obj = vm
2294 .compile(source, vm::compiler::Mode::Exec, "<embedded>".to_owned())
2295 .map_err(|err| vm.new_syntax_error(&err, Some(source)))
2296 .unwrap();
2297
2298 if let Err(e) = vm.run_code_obj(code_obj, scope) {
2299 vm.print_exception(e);
2300 panic!();
2301 }
2302 })
2303}
2304
2305#[test]
2306fn frozen_origname_matches() {
2307 use rustpython_vm as vm;
2308
2309 vm::Interpreter::builder(Default::default())
2310 .build()
2311 .enter(|vm| {
2312 let check = |name, expected| {
2313 let module = import::import_frozen(vm, name).unwrap();
2314 let origname: PyStrRef = module
2315 .get_attr("__origname__", vm)
2316 .unwrap()
2317 .try_into_value(vm)
2318 .unwrap();
2319 assert_eq!(origname.as_wtf8(), expected);
2320 };
2321
2322 check("_frozen_importlib", "importlib._bootstrap");
2323 check(
2324 "_frozen_importlib_external",
2325 "importlib._bootstrap_external",
2326 );
2327 });
2328}