1pub(crate) mod monitoring;
2
3use crate::{Py, PyPayload, PyResult, VirtualMachine, builtins::PyModule, convert::ToPyObject};
4
5#[cfg(all(not(feature = "host_env"), feature = "stdio"))]
6pub(crate) use sys::SandboxStdio;
7pub use sys::{COPYRIGHT, PLATFORM};
8pub(crate) use sys::{DOC, MAXSIZE, RUST_MULTIARCH, UnraisableHookArgsData, module_def, multiarch};
9
10#[pymodule(name = "_jit")]
11mod sys_jit {
12 #[pyfunction]
15 const fn is_available() -> bool {
16 false }
18
19 #[pyfunction]
22 const fn is_enabled() -> bool {
23 false }
25
26 #[pyfunction]
29 const fn is_active() -> bool {
30 false }
32}
33
34#[pymodule]
35pub mod sys {
36 use crate::{
37 AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyRefExact, PyResult,
38 builtins::{
39 PyBaseExceptionRef, PyDictRef, PyFrozenSet, PyNamespace, PyStr, PyStrRef, PyTuple,
40 PyTupleRef, PyTypeRef, PyUtf8StrRef,
41 },
42 common::{
43 ascii,
44 hash::{PyHash, PyUHash},
45 },
46 convert::ToPyObject,
47 frame::FrameObjectRef,
48 function::{FuncArgs, KwArgs, OptionalArg, PosArgs},
49 stdlib::{_warnings::warn, builtins},
50 types::PyStructSequence,
51 version,
52 vm::{Settings, VirtualMachine},
53 };
54 use core::ffi::CStr;
55 use core::sync::atomic::Ordering;
56 use num_traits::ToPrimitive;
57 use std::{
58 env,
59 io::{IsTerminal, Read, Write},
60 };
61
62 pub(crate) const RUST_MULTIARCH: &str = env!("RUSTPYTHON_TARGET_TRIPLE");
64
65 pub(crate) fn multiarch() -> String {
68 RUST_MULTIARCH.replace("-unknown", "")
69 }
70
71 #[pymodule(name = "monitoring", with(super::monitoring::sys_monitoring))]
72 pub(super) mod monitoring {}
73
74 #[pyclass(no_attr, name = "_BootstrapStderr")]
75 #[derive(Debug, PyPayload)]
76 pub(super) struct BootstrapStderr;
77
78 #[pyclass(with(Py))]
79 impl BootstrapStderr {}
80
81 #[pyclass]
82 impl Py<BootstrapStderr> {
83 #[pymethod]
84 fn write(&self, s: PyStrRef) -> usize {
85 let bytes = s.as_bytes();
86 let _ = std::io::stderr().write_all(bytes);
87 bytes.len()
88 }
89
90 #[pymethod]
91 fn flush(&self) {
92 let _ = std::io::stderr().flush();
93 }
94 }
95
96 #[pyclass(no_attr, name = "_SandboxStdio")]
99 #[derive(Debug, PyPayload)]
100 pub struct SandboxStdio {
101 pub fd: i32,
102 pub name: String,
103 pub mode: String,
104 }
105
106 #[pyclass(with(Py))]
107 impl SandboxStdio {}
108
109 #[pyclass]
110 impl Py<SandboxStdio> {
111 #[pymethod]
112 fn write(&self, s: PyStrRef, vm: &VirtualMachine) -> PyResult<usize> {
113 if self.fd == 0 {
114 return Err(vm.new_os_error("not writable"));
115 }
116 let bytes = s.as_bytes();
117 if self.fd == 2 {
118 std::io::stderr()
119 .write_all(bytes)
120 .map_err(|e| vm.new_os_error(e.to_string()))?;
121 } else {
122 std::io::stdout()
123 .write_all(bytes)
124 .map_err(|e| vm.new_os_error(e.to_string()))?;
125 }
126 Ok(bytes.len())
127 }
128
129 #[pymethod]
130 fn readline(&self, size: OptionalArg<isize>, vm: &VirtualMachine) -> PyResult<String> {
131 if self.fd != 0 {
132 return Err(vm.new_os_error("not readable"));
133 }
134 let size = size.unwrap_or(-1);
135 if size == 0 {
136 return Ok(String::new());
137 }
138 let mut line = String::new();
139 std::io::stdin()
140 .read_line(&mut line)
141 .map_err(|e| vm.new_os_error(e.to_string()))?;
142 if size > 0 {
143 line.truncate(size as usize);
144 }
145 Ok(line)
146 }
147
148 #[pymethod]
149 fn flush(&self, vm: &VirtualMachine) -> PyResult<()> {
150 match self.fd {
151 1 => {
152 std::io::stdout()
153 .flush()
154 .map_err(|e| vm.new_os_error(e.to_string()))?;
155 }
156 2 => {
157 std::io::stderr()
158 .flush()
159 .map_err(|e| vm.new_os_error(e.to_string()))?;
160 }
161 _ => {}
162 }
163 Ok(())
164 }
165
166 #[pymethod]
167 fn fileno(&self) -> i32 {
168 self.fd
169 }
170
171 #[pymethod]
172 fn isatty(&self) -> bool {
173 match self.fd {
174 0 => std::io::stdin().is_terminal(),
175 1 => std::io::stdout().is_terminal(),
176 2 => std::io::stderr().is_terminal(),
177 _ => false,
178 }
179 }
180
181 #[pymethod]
182 fn readable(&self) -> bool {
183 self.fd == 0
184 }
185
186 #[pymethod]
187 fn writable(&self) -> bool {
188 self.fd == 1 || self.fd == 2
189 }
190
191 #[pygetset]
192 fn closed(&self) -> bool {
193 false
194 }
195
196 #[pygetset]
197 fn encoding(&self) -> String {
198 "utf-8".to_owned()
199 }
200
201 #[pygetset]
202 fn errors(&self) -> String {
203 if self.fd == 2 {
204 "backslashreplace"
205 } else {
206 "strict"
207 }
208 .to_owned()
209 }
210
211 #[pygetset(name = "name")]
212 fn name_prop(&self) -> String {
213 self.name.clone()
214 }
215
216 #[pygetset(name = "mode")]
217 fn mode_prop(&self) -> String {
218 self.mode.clone()
219 }
220 }
221
222 #[pyattr(name = "_rustpython_debugbuild")]
223 const RUSTPYTHON_DEBUGBUILD: bool = cfg!(debug_assertions);
224
225 #[cfg(not(windows))]
226 #[pyattr(name = "abiflags")]
227 const ABIFLAGS_ATTR: &str = "t"; pub const ABIFLAGS: &str = "t";
230 #[pyattr(name = "api_version")]
231 const API_VERSION: u32 = 0x0; #[pyattr(name = "copyright")]
233 pub const COPYRIGHT: &CStr = c"Copyright (c) 2019 RustPython Team";
234 #[pyattr(name = "float_repr_style")]
235 const FLOAT_REPR_STYLE: &str = "short";
236 #[pyattr(name = "_framework")]
237 const FRAMEWORK: &str = "";
238 #[pyattr(name = "hexversion")]
239 const HEXVERSION: usize = version::VERSION_HEX;
240 #[pyattr(name = "maxsize")]
241 pub(crate) const MAXSIZE: isize = isize::MAX;
242 #[pyattr(name = "maxunicode")]
243 const MAXUNICODE: u32 = core::char::MAX as u32;
244
245 #[pyattr(name = "platform")]
246 pub const PLATFORM: &CStr = cfg_select! {
247 target_os = "linux" => c"linux",
248 target_os = "android" => c"android",
249 target_os = "macos" => c"darwin",
250 target_os = "ios" => c"ios",
251 windows => c"win32",
252 target_os = "wasi" => c"wasi",
253 _ => c"unknown"
254 };
255
256 #[pyattr(name = "ps1")]
257 const PS1: &str = ">>> ";
258 #[pyattr(name = "ps2")]
259 const PS2: &str = "... ";
260
261 #[cfg(windows)]
262 #[pyattr(name = "_vpath")]
263 const VPATH: Option<&'static str> = None; #[cfg(windows)]
266 #[pyattr(name = "dllhandle")]
267 const DLLHANDLE: usize = 0;
268
269 #[pyattr]
270 fn prefix(vm: &VirtualMachine) -> String {
271 vm.state.config.paths.prefix.clone()
272 }
273 #[pyattr]
274 fn base_prefix(vm: &VirtualMachine) -> String {
275 vm.state.config.paths.base_prefix.clone()
276 }
277 #[pyattr]
278 fn exec_prefix(vm: &VirtualMachine) -> String {
279 vm.state.config.paths.exec_prefix.clone()
280 }
281 #[pyattr]
282 fn base_exec_prefix(vm: &VirtualMachine) -> String {
283 vm.state.config.paths.base_exec_prefix.clone()
284 }
285 #[pyattr]
286 fn platlibdir(_vm: &VirtualMachine) -> &'static str {
287 option_env!("RUSTPYTHON_PLATLIBDIR").unwrap_or("lib")
288 }
289 #[pyattr]
290 fn _stdlib_dir(vm: &VirtualMachine) -> PyObjectRef {
291 vm.state.config.paths.stdlib_dir.clone().to_pyobject(vm)
292 }
293
294 #[pyattr]
297 fn argv(vm: &VirtualMachine) -> Vec<PyObjectRef> {
298 vm.state
299 .config
300 .settings
301 .argv
302 .iter()
303 .map(|arg| vm.ctx.new_str(arg.clone()).into())
304 .collect()
305 }
306
307 #[pyattr]
308 fn builtin_module_names(vm: &VirtualMachine) -> PyTupleRef {
309 let mut module_names: Vec<String> =
310 vm.state.module_defs.keys().map(|&s| s.to_owned()).collect();
311 module_names.push("sys".to_owned());
312 module_names.push("builtins".to_owned());
313
314 module_names.sort();
315 vm.ctx.new_tuple(
316 module_names
317 .into_iter()
318 .map(|n| vm.ctx.new_utf8_str(n).into())
319 .collect(),
320 )
321 }
322
323 const STDLIB_MODULE_NAMES: &[&str] = &[
325 "__future__",
326 "_abc",
327 "_aix_support",
328 "_android_support",
329 "_apple_support",
330 "_ast",
331 "_asyncio",
332 "_bisect",
333 "_blake2",
334 "_bz2",
335 "_codecs",
336 "_codecs_cn",
337 "_codecs_hk",
338 "_codecs_iso2022",
339 "_codecs_jp",
340 "_codecs_kr",
341 "_codecs_tw",
342 "_collections",
343 "_collections_abc",
344 "_colorize",
345 "_compat_pickle",
346 "_compression",
347 "_contextvars",
348 "_csv",
349 "_ctypes",
350 "_curses",
351 "_curses_panel",
352 "_datetime",
353 "_dbm",
354 "_decimal",
355 "_elementtree",
356 "_frozen_importlib",
357 "_frozen_importlib_external",
358 "_functools",
359 "_gdbm",
360 "_hashlib",
361 "_heapq",
362 "_imp",
363 "_interpchannels",
364 "_interpqueues",
365 "_interpreters",
366 "_io",
367 "_ios_support",
368 "_json",
369 "_locale",
370 "_lsprof",
371 "_lzma",
372 "_markupbase",
373 "_md5",
374 "_multibytecodec",
375 "_multiprocessing",
376 "_opcode",
377 "_opcode_metadata",
378 "_operator",
379 "_osx_support",
380 "_overlapped",
381 "_pickle",
382 "_posixshmem",
383 "_posixsubprocess",
384 "_py_abc",
385 "_pydatetime",
386 "_pydecimal",
387 "_pyio",
388 "_pylong",
389 "_pyrepl",
390 "_queue",
391 "_random",
392 "_scproxy",
393 "_sha1",
394 "_sha2",
395 "_sha3",
396 "_signal",
397 "_sitebuiltins",
398 "_socket",
399 "_sqlite3",
400 "_sre",
401 "_ssl",
402 "_stat",
403 "_statistics",
404 "_string",
405 "_strptime",
406 "_struct",
407 "_suggestions",
408 "_symtable",
409 "_sysconfig",
410 "_testinternalcapi",
411 "_thread",
412 "_threading_local",
413 "_tkinter",
414 "_tokenize",
415 "_tracemalloc",
416 "_typing",
417 "_uuid",
418 "_warnings",
419 "_weakref",
420 "_weakrefset",
421 "_winapi",
422 "_wmi",
423 "_zoneinfo",
424 "abc",
425 "antigravity",
426 "argparse",
427 "array",
428 "ast",
429 "asyncio",
430 "atexit",
431 "base64",
432 "bdb",
433 "binascii",
434 "bisect",
435 "builtins",
436 "bz2",
437 "cProfile",
438 "calendar",
439 "cmath",
440 "cmd",
441 "code",
442 "codecs",
443 "codeop",
444 "collections",
445 "colorsys",
446 "compileall",
447 "concurrent",
448 "configparser",
449 "contextlib",
450 "contextvars",
451 "copy",
452 "copyreg",
453 "csv",
454 "ctypes",
455 "curses",
456 "dataclasses",
457 "datetime",
458 "dbm",
459 "decimal",
460 "difflib",
461 "dis",
462 "doctest",
463 "email",
464 "encodings",
465 "ensurepip",
466 "enum",
467 "errno",
468 "faulthandler",
469 "fcntl",
470 "filecmp",
471 "fileinput",
472 "fnmatch",
473 "fractions",
474 "ftplib",
475 "functools",
476 "gc",
477 "genericpath",
478 "getopt",
479 "getpass",
480 "gettext",
481 "glob",
482 "graphlib",
483 "grp",
484 "gzip",
485 "hashlib",
486 "heapq",
487 "hmac",
488 "html",
489 "http",
490 "idlelib",
491 "imaplib",
492 "importlib",
493 "inspect",
494 "io",
495 "ipaddress",
496 "itertools",
497 "json",
498 "keyword",
499 "linecache",
500 "locale",
501 "logging",
502 "lzma",
503 "mailbox",
504 "marshal",
505 "math",
506 "mimetypes",
507 "mmap",
508 "modulefinder",
509 "msvcrt",
510 "multiprocessing",
511 "netrc",
512 "nt",
513 "ntpath",
514 "nturl2path",
515 "numbers",
516 "opcode",
517 "operator",
518 "optparse",
519 "os",
520 "pathlib",
521 "pdb",
522 "pickle",
523 "pickletools",
524 "pkgutil",
525 "platform",
526 "plistlib",
527 "poplib",
528 "posix",
529 "posixpath",
530 "pprint",
531 "profile",
532 "pstats",
533 "pty",
534 "pwd",
535 "py_compile",
536 "pyclbr",
537 "pydoc",
538 "pydoc_data",
539 "pyexpat",
540 "queue",
541 "quopri",
542 "random",
543 "re",
544 "readline",
545 "reprlib",
546 "resource",
547 "rlcompleter",
548 "runpy",
549 "sched",
550 "secrets",
551 "select",
552 "selectors",
553 "shelve",
554 "shlex",
555 "shutil",
556 "signal",
557 "site",
558 "smtplib",
559 "socket",
560 "socketserver",
561 "sqlite3",
562 "sre_compile",
563 "sre_constants",
564 "sre_parse",
565 "ssl",
566 "stat",
567 "statistics",
568 "string",
569 "stringprep",
570 "struct",
571 "subprocess",
572 "symtable",
573 "sys",
574 "sysconfig",
575 "syslog",
576 "tabnanny",
577 "tarfile",
578 "tempfile",
579 "termios",
580 "textwrap",
581 "this",
582 "threading",
583 "time",
584 "timeit",
585 "tkinter",
586 "token",
587 "tokenize",
588 "tomllib",
589 "trace",
590 "traceback",
591 "tracemalloc",
592 "tty",
593 "turtle",
594 "turtledemo",
595 "types",
596 "typing",
597 "unicodedata",
598 "unittest",
599 "urllib",
600 "uuid",
601 "venv",
602 "warnings",
603 "wave",
604 "weakref",
605 "webbrowser",
606 "winreg",
607 "winsound",
608 "wsgiref",
609 "xml",
610 "xmlrpc",
611 "zipapp",
612 "zipfile",
613 "zipimport",
614 "zlib",
615 "zoneinfo",
616 ];
617
618 #[pyattr(once)]
619 fn stdlib_module_names(vm: &VirtualMachine) -> PyObjectRef {
620 let names = STDLIB_MODULE_NAMES
621 .iter()
622 .map(|&n| vm.ctx.new_str(n).into());
623 PyFrozenSet::from_iter(vm, names)
624 .expect("Creating stdlib_module_names frozen set must succeed")
625 .to_pyobject(vm)
626 }
627
628 #[pyattr]
629 fn byteorder(vm: &VirtualMachine) -> PyStrRef {
630 vm.ctx
632 .intern_str(if cfg!(target_endian = "little") {
633 "little"
634 } else if cfg!(target_endian = "big") {
635 "big"
636 } else {
637 "unknown"
638 })
639 .to_owned()
640 }
641
642 #[pyattr]
643 fn _base_executable(vm: &VirtualMachine) -> String {
644 vm.state.config.paths.base_executable.clone()
645 }
646
647 #[pyattr]
648 fn dont_write_bytecode(vm: &VirtualMachine) -> bool {
649 !vm.state.config.settings.write_bytecode
650 }
651
652 #[pyattr]
653 fn executable(vm: &VirtualMachine) -> String {
654 vm.state.config.paths.executable.clone()
655 }
656
657 #[pyattr]
658 fn _git(vm: &VirtualMachine) -> PyTupleRef {
659 vm.new_tuple((
660 ascii!("RustPython"),
661 version::GIT_IDENTIFIER,
662 version::GIT_REVISION,
663 ))
664 }
665
666 #[pyattr]
667 fn implementation(vm: &VirtualMachine) -> PyRef<PyNamespace> {
668 const NAME: &str = "rustpython";
669
670 let cache_tag = format!("cpython-{}{}", version::MAJOR, version::MINOR);
672 let ctx = &vm.ctx;
673 py_namespace!(vm, {
674 "name" => ctx.new_str(NAME),
675 "cache_tag" => ctx.new_str(cache_tag),
676 "_multiarch" => ctx.new_str(multiarch()),
677 "version" => PyVersionInfo::from_data(VersionInfoData::IMPLEMENTATION, vm),
678 "hexversion" => ctx.new_int(version::VERSION_HEX_IMPL),
679 "supports_isolated_interpreters" =>
680 ctx.new_bool(crate::vm::runtime::SUPPORTS_ISOLATED_INTERPRETERS),
681 })
682 }
683
684 #[pyattr]
685 const fn meta_path(_vm: &VirtualMachine) -> Vec<PyObjectRef> {
686 Vec::new()
687 }
688
689 #[pyattr]
690 fn orig_argv(vm: &VirtualMachine) -> Vec<PyObjectRef> {
691 env::args().map(|arg| vm.ctx.new_str(arg).into()).collect()
692 }
693
694 #[pyattr]
695 fn path(vm: &VirtualMachine) -> Vec<PyObjectRef> {
696 vm.state
697 .config
698 .paths
699 .module_search_paths
700 .iter()
701 .map(|path| vm.ctx.new_str(path.clone()).into())
702 .collect()
703 }
704
705 #[pyattr]
706 const fn path_hooks(_vm: &VirtualMachine) -> Vec<PyObjectRef> {
707 Vec::new()
708 }
709
710 #[pyattr]
711 fn path_importer_cache(vm: &VirtualMachine) -> PyDictRef {
712 vm.ctx.new_dict()
713 }
714
715 #[pyattr]
716 fn pycache_prefix(vm: &VirtualMachine) -> PyObjectRef {
717 vm.ctx.none()
718 }
719
720 #[pyattr(name = "version")]
721 const VERSION: &str = version::RUSTPYTHON_VERSION;
722
723 #[cfg(windows)]
725 #[pyattr(name = "winver")]
726 const WINVER: &str = version::WINVER;
727
728 #[pyattr]
729 fn _xoptions(vm: &VirtualMachine) -> PyDictRef {
730 let ctx = &vm.ctx;
731 let xopts = ctx.new_dict();
732 for (key, value) in &vm.state.config.settings.xoptions {
733 let value = value.as_ref().map_or_else(
734 || ctx.new_bool(true).into(),
735 |s| ctx.new_str(s.clone()).into(),
736 );
737 xopts.set_item(&**key, value, vm).unwrap();
738 }
739 xopts
740 }
741
742 #[pyattr]
743 fn warnoptions(vm: &VirtualMachine) -> Vec<PyObjectRef> {
744 vm.state
745 .config
746 .settings
747 .warnoptions
748 .iter()
749 .map(|s| vm.ctx.new_str(s.clone()).into())
750 .collect()
751 }
752
753 #[cfg(feature = "rustpython-compiler")]
754 #[pyfunction]
755 fn _baserepl(vm: &VirtualMachine) -> PyResult<()> {
756 let stdin = std::io::stdin();
758 let mut handle = stdin.lock();
759 let mut source = String::new();
760 handle
761 .read_to_string(&mut source)
762 .map_err(|e| vm.new_os_error(format!("Error reading from stdin: {e}")))?;
763 vm.compile(&source, crate::compiler::Mode::Single, "<stdin>")
764 .map_err(|e| e.into_pyexception(vm, Some(&source)))?;
765 Ok(())
766 }
767
768 #[pyfunction]
769 const fn _is_gil_enabled() -> bool {
770 false }
772
773 #[pyfunction]
774 const fn is_remote_debug_enabled() -> bool {
775 false }
777
778 #[derive(FromArgs)]
779 struct ExitArgs {
780 #[pyarg(positional, optional)]
781 status: Option<PyObjectRef>,
782 }
783
784 #[derive(FromArgs)]
785 struct GetFrameArgs {
786 #[pyarg(positional, default)]
787 depth: usize,
788 }
789
790 #[derive(FromArgs)]
791 struct GetFrameModuleNameArgs {
792 #[pyarg(any, default)]
793 depth: usize,
794 }
795
796 #[derive(FromArgs)]
797 struct SetMaxDigitsArgs {
798 #[pyarg(any)]
799 maxdigits: usize,
800 }
801
802 #[derive(FromArgs)]
803 struct SetDepthArgs {
804 #[pyarg(any)]
805 depth: i32,
806 }
807
808 #[derive(FromArgs)]
809 struct AuditHookArgs {
810 #[pyarg(any)]
811 hook: PyObjectRef,
812 }
813
814 #[pyfunction]
815 fn exit(args: ExitArgs, vm: &VirtualMachine) -> PyResult {
816 let status = args.status.unwrap_or_else(|| vm.ctx.none());
817 let args = if let Some(status_tuple) = status.downcast_ref::<PyTuple>() {
818 status_tuple.as_slice().to_vec()
819 } else {
820 vec![status]
821 };
822 Err(vm.new_system_exit(args.into()))
823 }
824
825 #[pyfunction]
826 fn call_tracing(func: PyObjectRef, args: PyTupleRef, vm: &VirtualMachine) -> PyResult {
827 func.call(PosArgs::new(args.as_slice().to_vec()), vm)
831 }
832
833 #[pyfunction]
834 fn exception(vm: &VirtualMachine) -> Option<PyBaseExceptionRef> {
835 vm.topmost_exception()
836 }
837
838 #[pyfunction(name = "__displayhook__")]
839 #[pyfunction]
840 fn displayhook(object: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
841 if vm.is_none(&object) {
843 return Ok(());
844 }
845 vm.builtins.set_attr("_", vm.ctx.none(), vm)?;
847 let repr = object.repr(vm)?.into();
849 builtins::print(PosArgs::new(vec![repr]), Default::default(), vm)?;
850 vm.builtins.set_attr("_", object, vm)?;
851 Ok(())
852 }
853
854 #[pyfunction(name = "__excepthook__")]
855 #[pyfunction]
856 fn excepthook(
857 exctype: PyObjectRef,
858 value: PyObjectRef,
859 traceback: PyObjectRef,
860 vm: &VirtualMachine,
861 ) -> PyResult<()> {
862 let stderr = super::get_stderr(vm)?;
863 let has_traceback = !vm.is_none(&traceback);
865 match vm.normalize_exception(exctype, value.clone(), traceback) {
866 Ok(exc) => {
867 let native_syntax_error_display = !has_traceback
868 && exc.fast_isinstance(vm.ctx.exceptions.syntax_error)
869 && exc
870 .as_object()
871 .get_attr("msg", vm)
872 .ok()
873 .and_then(|msg| msg.downcast::<PyStr>().ok())
874 .is_some_and(|msg| msg.to_string_lossy() == "unexpected EOF while parsing")
875 && exc
876 .as_object()
877 .get_attr("text", vm)
878 .ok()
879 .and_then(|text| text.downcast::<PyStr>().ok())
880 .is_some_and(|text| text.to_string_lossy().trim_end() == "\\");
881 if native_syntax_error_display {
882 return vm.write_exception(&mut crate::py_io::PyWriter(stderr, vm), &exc);
883 }
884 if let Ok(tb_mod) = vm.import("traceback", 0)
886 && let Ok(print_exc_builtin) = tb_mod.get_attr("_print_exception_bltin", vm)
887 && print_exc_builtin
888 .call((exc.as_object().to_owned(),), vm)
889 .is_ok()
890 {
891 return Ok(());
892 }
893 vm.write_exception(&mut crate::py_io::PyWriter(stderr, vm), &exc)
895 }
896 Err(_) => {
897 let type_name = value.class().name();
898 let msg = format!(
899 "TypeError: print_exception(): Exception expected for value, {type_name} found\n"
900 );
901 use crate::py_io::Write;
902 write!(&mut crate::py_io::PyWriter(stderr, vm), "{msg}")?;
903 Ok(())
904 }
905 }
906 }
907
908 #[pyfunction(name = "__breakpointhook__")]
909 #[pyfunction]
910 pub fn breakpointhook(args: FuncArgs, vm: &VirtualMachine) -> PyResult {
911 #[cfg(feature = "host_env")]
912 let env_var = crate::host_env::os::var("PYTHONBREAKPOINT")
913 .and_then(|env_var| {
914 if env_var.is_empty() {
915 Err(std::env::VarError::NotPresent)
916 } else {
917 Ok(env_var)
918 }
919 })
920 .unwrap_or_else(|_| "pdb.set_trace".to_owned());
921 #[cfg(not(feature = "host_env"))]
922 let env_var = "pdb.set_trace".to_owned();
923
924 if env_var.eq("0") {
925 return Ok(vm.ctx.none());
926 };
927
928 let print_unimportable_module_warn = || {
929 warn(
930 vm.ctx.exceptions.runtime_warning,
931 format!("Ignoring unimportable $PYTHONBREAKPOINT: \"{env_var}\"",),
932 0,
933 vm,
934 )?;
935 Ok(vm.ctx.none())
936 };
937
938 let last = match env_var.rsplit_once('.') {
939 Some((_, last)) => last,
940 None if !env_var.is_empty() => env_var.as_str(),
941 _ => return print_unimportable_module_warn(),
942 };
943
944 let (module_path, attr_name) = if last == env_var {
945 ("builtins", env_var.as_str())
946 } else {
947 (&env_var[..(env_var.len() - last.len() - 1)], last)
948 };
949
950 let module = match vm.import(&vm.ctx.new_str(module_path), 0) {
951 Ok(module) => module,
952 Err(_) => {
953 return print_unimportable_module_warn();
954 }
955 };
956
957 match vm.get_attribute_opt(&module, &vm.ctx.new_str(attr_name)) {
958 Ok(Some(hook)) => hook.as_ref().call(args, vm),
959 _ => print_unimportable_module_warn(),
960 }
961 }
962
963 #[pyfunction]
964 fn exc_info(vm: &VirtualMachine) -> (PyObjectRef, PyObjectRef, PyObjectRef) {
965 match vm.topmost_exception() {
966 Some(exception) => vm.split_exception(exception),
967 None => (vm.ctx.none(), vm.ctx.none(), vm.ctx.none()),
968 }
969 }
970
971 #[pyattr]
972 fn flags(vm: &VirtualMachine) -> PyTupleRef {
973 PyFlags::from_data(FlagsData::from_settings(&vm.state.config.settings), vm)
974 }
975
976 #[pyattr]
977 fn float_info(vm: &VirtualMachine) -> PyTupleRef {
978 PyFloatInfo::from_data(FloatInfoData::INFO, vm)
979 }
980
981 #[pyfunction]
982 const fn getdefaultencoding() -> &'static str {
983 crate::codecs::DEFAULT_ENCODING
984 }
985
986 #[pyfunction]
987 fn getrefcount(object: PyObjectRef) -> usize {
988 object.strong_count()
989 }
990
991 #[pyfunction]
992 fn getrecursionlimit(vm: &VirtualMachine) -> usize {
993 vm.recursion_limit.get()
994 }
995
996 #[derive(FromArgs)]
997 struct GetsizeofArgs {
998 object: PyObjectRef,
999 #[pyarg(any, optional)]
1000 default: Option<PyObjectRef>,
1001 }
1002
1003 #[pyfunction]
1004 fn getsizeof(args: GetsizeofArgs, vm: &VirtualMachine) -> PyResult {
1005 let sizeof = || -> PyResult<usize> {
1006 let res = vm.call_special_method(&args.object, identifier!(vm, __sizeof__), ())?;
1007 let res = res.try_index(vm)?.try_to_primitive::<usize>(vm)?;
1008 Ok(res + core::mem::size_of::<PyObject>())
1009 };
1010 sizeof()
1011 .map(|x| vm.ctx.new_int(x).into())
1012 .or_else(|err| args.default.ok_or(err))
1013 }
1014
1015 #[pyfunction]
1016 fn getfilesystemencoding(vm: &VirtualMachine) -> PyStrRef {
1017 vm.fs_encoding().to_owned()
1018 }
1019
1020 #[pyfunction]
1021 fn getfilesystemencodeerrors(vm: &VirtualMachine) -> PyUtf8StrRef {
1022 vm.fs_encode_errors().to_owned()
1023 }
1024
1025 #[pyfunction]
1026 fn getprofile(vm: &VirtualMachine) -> PyObjectRef {
1027 #[cfg(feature = "threading")]
1028 if let Some(slot) = crate::vm::thread::current_thread_slot() {
1029 return slot.profile_func.lock().clone();
1030 }
1031 vm.profile_func.borrow().clone()
1032 }
1033
1034 #[pyfunction]
1035 fn _getframe(args: GetFrameArgs, vm: &VirtualMachine) -> PyResult<FrameObjectRef> {
1036 let depth = args.depth;
1037 let frame_ref = crate::frame::frame_at_offset(depth, vm)
1038 .ok_or_else(|| vm.new_value_error("call stack is not deep enough"))?;
1039 vm.audit("sys._getframe", || (frame_ref.to_owned(),))?;
1040
1041 Ok(frame_ref)
1042 }
1043
1044 #[pyfunction]
1045 fn _getframemodulename(
1046 args: GetFrameModuleNameArgs,
1047 vm: &VirtualMachine,
1048 ) -> PyResult<PyObjectRef> {
1049 let depth = args.depth;
1050 vm.audit("sys._getframemodulename", || (depth,))?;
1051
1052 let func_obj = match crate::frame::frame_at_offset(depth, vm) {
1054 Some(frame) => frame.iframe().func_obj().map(|o| o.to_owned()),
1055 None => return Ok(vm.ctx.none()),
1056 };
1057
1058 Ok(if let Some(func_obj) = func_obj {
1060 func_obj
1061 .get_attr(identifier!(vm, __module__), vm)
1062 .unwrap_or_else(
1063 |_| vm.ctx.none(),
1065 )
1066 } else {
1067 vm.ctx.none()
1068 })
1069 }
1070
1071 #[cfg(feature = "threading")]
1072 #[pyfunction]
1073 fn _current_frames(vm: &VirtualMachine) -> PyResult<PyDictRef> {
1074 use crate::AsObject;
1075 use crate::stdlib::_thread::get_all_current_frames;
1076
1077 let frames = get_all_current_frames(vm);
1078 let dict = vm.ctx.new_dict();
1079
1080 for (thread_id, frame) in frames {
1081 let key = vm.ctx.new_int(thread_id);
1082 dict.set_item(key.as_object(), frame.into(), vm)?;
1083 }
1084
1085 Ok(dict)
1086 }
1087
1088 #[cfg(feature = "threading")]
1089 #[pyfunction]
1090 fn _current_exceptions(vm: &VirtualMachine) -> PyResult<PyDictRef> {
1091 use crate::AsObject;
1092 use crate::vm::thread::get_all_current_exceptions;
1093
1094 let dict = vm.ctx.new_dict();
1095 for (thread_id, exc) in get_all_current_exceptions(vm) {
1096 let key = vm.ctx.new_int(thread_id);
1097 let value = exc.map_or_else(|| vm.ctx.none(), |e| e.into());
1098 dict.set_item(key.as_object(), value, vm)?;
1099 }
1100
1101 Ok(dict)
1102 }
1103
1104 #[cfg(not(feature = "threading"))]
1105 #[pyfunction]
1106 fn _current_exceptions(vm: &VirtualMachine) -> PyResult<PyDictRef> {
1107 let dict = vm.ctx.new_dict();
1108 let key = vm.ctx.new_int(0);
1109 dict.set_item(key.as_object(), vm.topmost_exception().to_pyobject(vm), vm)?;
1110 Ok(dict)
1111 }
1112
1113 #[cfg(not(feature = "threading"))]
1115 #[pyfunction]
1116 fn _current_frames(vm: &VirtualMachine) -> PyDictRef {
1117 vm.ctx.new_dict()
1118 }
1119
1120 #[pyfunction]
1121 fn gettrace(vm: &VirtualMachine) -> PyObjectRef {
1122 #[cfg(feature = "threading")]
1123 if let Some(slot) = crate::vm::thread::current_thread_slot() {
1124 return slot.trace_func.lock().clone();
1125 }
1126 vm.trace_func.borrow().clone()
1127 }
1128
1129 #[cfg(windows)]
1130 #[pyfunction]
1131 fn getwindowsversion(vm: &VirtualMachine) -> PyResult<crate::builtins::tuple::PyTupleRef> {
1132 let version = crate::host_env::windows::get_windows_version()
1133 .map_err(|e| vm.new_os_error(e.to_string()))?;
1134 let winver = WindowsVersionData {
1135 major: version.major,
1136 minor: version.minor,
1137 build: version.build,
1138 platform: version.platform,
1139 service_pack: version.service_pack,
1140 service_pack_major: version.service_pack_major,
1141 service_pack_minor: version.service_pack_minor,
1142 suite_mask: version.suite_mask,
1143 product_type: version.product_type,
1144 platform_version: (version.major, version.minor, version.build), };
1146 Ok(PyWindowsVersion::from_data(winver, vm))
1147 }
1148
1149 fn _unraisablehook(unraisable: UnraisableHookArgsData, vm: &VirtualMachine) -> PyResult<()> {
1150 use super::PyStderr;
1151
1152 let stderr = PyStderr(vm);
1153 if !vm.is_none(&unraisable.object) {
1154 if !vm.is_none(&unraisable.err_msg) {
1155 write!(stderr, "{}: ", unraisable.err_msg.str(vm)?);
1156 } else {
1157 write!(stderr, "Exception ignored in: ");
1158 }
1159 let repr = &unraisable.object.repr(vm);
1161 let str = match repr {
1162 Ok(v) => v.to_string(),
1163 Err(_) => format!(
1164 "<object {} repr() failed>",
1165 unraisable.object.class().name()
1166 ),
1167 };
1168 writeln!(stderr, "{str}");
1169 } else if !vm.is_none(&unraisable.err_msg) {
1170 writeln!(stderr, "{}:", unraisable.err_msg.str(vm)?);
1171 }
1172
1173 if !vm.is_none(&unraisable.exc_traceback) {
1175 let tb_module = vm.import("traceback", 0)?;
1176 let print_tb = tb_module.get_attr("print_tb", vm)?;
1177 let stderr_obj = super::get_stderr(vm)?;
1178 let kwargs: KwArgs = core::iter::once(("file".to_string(), stderr_obj)).collect();
1179 let _ = print_tb.call(
1180 FuncArgs::new(vec![unraisable.exc_traceback.clone()], kwargs),
1181 vm,
1182 );
1183 }
1184
1185 if vm.is_none(unraisable.exc_type.as_object()) {
1187 return Ok(());
1188 }
1189 assert!(
1190 unraisable
1191 .exc_type
1192 .fast_issubclass(vm.ctx.exceptions.base_exception_type)
1193 );
1194
1195 let module_name = unraisable.exc_type.__module__(vm).ok();
1199 if let Some(module_str) = module_name
1200 .as_ref()
1201 .and_then(|name| name.downcast_ref::<PyStr>())
1202 {
1203 let module = module_str.as_wtf8();
1204 if module != "builtins" && module != "__main__" {
1205 write!(stderr, "{module}.");
1206 }
1207 } else {
1208 write!(stderr, "<unknown>");
1209 }
1210
1211 let qualname = unraisable.exc_type.__qualname__(vm);
1213 if let Ok(qualname_str) = qualname.downcast::<PyStr>() {
1214 write!(stderr, "{}", qualname_str.as_wtf8());
1215 } else {
1216 write!(stderr, "{}", unraisable.exc_type.name());
1217 }
1218
1219 if !vm.is_none(&unraisable.exc_value) {
1221 write!(stderr, ": ");
1222 if let Ok(str) = unraisable.exc_value.str(vm) {
1223 write!(stderr, "{}", str.as_wtf8());
1224 } else {
1225 write!(stderr, "<exception str() failed>");
1226 }
1227 }
1228 writeln!(stderr);
1229
1230 if let Ok(stderr_obj) = super::get_stderr(vm)
1232 && let Ok(flush) = stderr_obj.get_attr("flush", vm)
1233 {
1234 let _ = flush.call((), vm);
1235 }
1236
1237 Ok(())
1238 }
1239
1240 #[pyattr]
1241 #[pyfunction(name = "__unraisablehook__")]
1242 fn unraisablehook(unraisable: UnraisableHookArgsData, vm: &VirtualMachine) {
1243 if let Err(e) = _unraisablehook(unraisable, vm) {
1244 let stderr = super::PyStderr(vm);
1245 writeln!(
1246 stderr,
1247 "{}",
1248 e.as_object()
1249 .repr(vm)
1250 .unwrap_or_else(|_| vm.ctx.empty_str.to_owned())
1251 );
1252 }
1253 }
1254
1255 #[pyattr]
1256 fn hash_info(vm: &VirtualMachine) -> PyTupleRef {
1257 PyHashInfo::from_data(HashInfoData::INFO, vm)
1258 }
1259
1260 #[pyfunction]
1261 fn intern(string: PyRefExact<PyStr>, vm: &VirtualMachine) -> PyRef<PyStr> {
1262 vm.ctx.intern_str(string).to_owned()
1263 }
1264
1265 #[pyattr]
1266 fn int_info(vm: &VirtualMachine) -> PyTupleRef {
1267 PyIntInfo::from_data(IntInfoData::INFO, vm)
1268 }
1269
1270 #[pyfunction]
1272 fn _get_cpu_count_config(vm: &VirtualMachine) -> i32 {
1273 vm.state.config.settings.cpu_count.map_or(-1, |n| n.get())
1274 }
1275
1276 #[pyfunction]
1277 fn get_int_max_str_digits(vm: &VirtualMachine) -> usize {
1278 vm.state.int_max_str_digits.load()
1279 }
1280
1281 #[pyfunction]
1282 fn set_int_max_str_digits(
1283 SetMaxDigitsArgs { maxdigits }: SetMaxDigitsArgs,
1284 vm: &VirtualMachine,
1285 ) -> PyResult<()> {
1286 let threshold = IntInfoData::INFO.str_digits_check_threshold;
1287 if maxdigits == 0 || maxdigits >= threshold {
1288 vm.state.int_max_str_digits.store(maxdigits);
1289 Ok(())
1290 } else {
1291 let error = format!("maxdigits must be 0 or larger than {threshold}");
1292 Err(vm.new_value_error(error))
1293 }
1294 }
1295
1296 #[pyfunction]
1297 fn is_finalizing(vm: &VirtualMachine) -> bool {
1298 vm.state.finalizing.load(Ordering::Acquire)
1299 }
1300
1301 #[pyfunction]
1302 fn setprofile(function: PyObjectRef, vm: &VirtualMachine) {
1303 #[cfg(feature = "threading")]
1304 if let Some(slot) = crate::vm::thread::current_thread_slot() {
1305 *slot.profile_func.lock() = function.clone();
1306 }
1307 vm.profile_func.replace(function);
1308 update_use_tracing(vm);
1309 }
1310
1311 #[pyfunction]
1312 fn setrecursionlimit(limit: i32, vm: &VirtualMachine) -> PyResult<()> {
1313 let limit = limit.to_usize().filter(|&u| u >= 1).ok_or_else(|| {
1314 vm.new_value_error("recursion limit must be greater than or equal to one")
1315 })?;
1316 let recursion_depth = vm.current_recursion_depth();
1317
1318 if limit > recursion_depth {
1319 vm.recursion_limit.set(limit);
1320 Ok(())
1321 } else {
1322 Err(vm.new_recursion_error(format!(
1323 "cannot set the recursion limit to {limit} at the recursion depth {recursion_depth}: the limit is too low"
1324 )))
1325 }
1326 }
1327
1328 #[pyfunction]
1329 fn settrace(function: PyObjectRef, vm: &VirtualMachine) {
1330 #[cfg(feature = "threading")]
1331 if let Some(slot) = crate::vm::thread::current_thread_slot() {
1332 *slot.trace_func.lock() = function.clone();
1333 }
1334 vm.trace_func.replace(function);
1335 update_use_tracing(vm);
1336 if let Some(frame) = vm.current_frame() {
1340 frame.iframe().sync_prev_line_from_lasti();
1341 }
1342 }
1343
1344 #[pyfunction]
1345 fn _settraceallthreads(function: PyObjectRef, vm: &VirtualMachine) {
1346 let func = (!vm.is_none(&function)).then(|| function.clone());
1347 *vm.state.global_trace_func.lock() = func;
1348 #[cfg(feature = "threading")]
1349 {
1350 let registry = vm.state.thread_frames.lock();
1351 #[expect(
1352 clippy::iter_over_hash_type,
1353 reason = "every thread slot, order is irrelevant"
1354 )]
1355 for slot in registry.values() {
1356 *slot.trace_func.lock() = function.clone();
1357 }
1358 }
1359 vm.trace_func.replace(function);
1360 update_use_tracing(vm);
1361 }
1362
1363 #[pyfunction]
1364 fn _setprofileallthreads(function: PyObjectRef, vm: &VirtualMachine) {
1365 let func = (!vm.is_none(&function)).then(|| function.clone());
1366 *vm.state.global_profile_func.lock() = func;
1367 #[cfg(feature = "threading")]
1368 {
1369 let registry = vm.state.thread_frames.lock();
1370 #[expect(
1371 clippy::iter_over_hash_type,
1372 reason = "every thread slot, order is irrelevant"
1373 )]
1374 for slot in registry.values() {
1375 *slot.profile_func.lock() = function.clone();
1376 }
1377 }
1378 vm.profile_func.replace(function);
1379 update_use_tracing(vm);
1380 }
1381
1382 #[cfg(feature = "threading")]
1383 #[pyattr]
1384 fn thread_info(vm: &VirtualMachine) -> PyTupleRef {
1385 PyThreadInfo::from_data(ThreadInfoData::INFO, vm)
1386 }
1387
1388 #[pyattr]
1389 fn version_info(vm: &VirtualMachine) -> PyTupleRef {
1390 PyVersionInfo::from_data(VersionInfoData::VERSION, vm)
1391 }
1392
1393 fn update_use_tracing(vm: &VirtualMachine) {
1394 let trace_is_none = vm.is_none(&vm.trace_func.borrow());
1395 let profile_is_none = vm.is_none(&vm.profile_func.borrow());
1396 let tracing = !(trace_is_none && profile_is_none);
1397 vm.use_tracing.set(tracing);
1398 }
1399
1400 #[pyfunction]
1401 fn set_coroutine_origin_tracking_depth(
1402 SetDepthArgs { depth }: SetDepthArgs,
1403 vm: &VirtualMachine,
1404 ) -> PyResult<()> {
1405 if depth < 0 {
1406 return Err(vm.new_value_error("depth must be >= 0"));
1407 }
1408 crate::vm::thread::COROUTINE_ORIGIN_TRACKING_DEPTH.set(depth as u32);
1409 Ok(())
1410 }
1411
1412 #[pyfunction]
1413 fn get_coroutine_origin_tracking_depth() -> i32 {
1414 crate::vm::thread::COROUTINE_ORIGIN_TRACKING_DEPTH.get() as i32
1415 }
1416
1417 #[pyfunction]
1418 fn _clear_type_descriptors(r#type: PyTypeRef, vm: &VirtualMachine) -> PyResult<()> {
1419 use crate::types::PyTypeFlags;
1420
1421 if r#type.slots.flags.has_feature(PyTypeFlags::IMMUTABLETYPE) {
1423 return Err(vm.new_type_error("argument is immutable"));
1424 }
1425
1426 r#type.attributes.remove(identifier!(vm, __dict__));
1428
1429 r#type.attributes.remove(identifier!(vm, __weakref__));
1431
1432 r#type.update_slot::<true>(identifier!(vm, __dict__), &vm.ctx);
1434 r#type.update_slot::<true>(identifier!(vm, __weakref__), &vm.ctx);
1435
1436 Ok(())
1437 }
1438
1439 #[pyfunction]
1440 fn getswitchinterval(vm: &VirtualMachine) -> f64 {
1441 vm.state.switch_interval.load()
1443 }
1444
1445 #[pyfunction]
1447 fn setswitchinterval(interval: f64, vm: &VirtualMachine) -> PyResult<()> {
1448 if interval <= 0.0 {
1450 return Err(vm.new_value_error("switch interval must be strictly positive"));
1451 }
1452
1453 vm.state.switch_interval.store(interval);
1455 Ok(())
1456 }
1457
1458 #[derive(FromArgs)]
1459 struct SetAsyncgenHooksArgs {
1460 #[pyarg(any, optional)]
1461 firstiter: OptionalArg<Option<PyObjectRef>>,
1462 #[pyarg(any, optional)]
1463 finalizer: OptionalArg<Option<PyObjectRef>>,
1464 }
1465
1466 #[pyfunction]
1467 fn set_asyncgen_hooks(args: SetAsyncgenHooksArgs, vm: &VirtualMachine) -> PyResult<()> {
1468 if let Some(Some(finalizer)) = args.finalizer.as_option()
1469 && !finalizer.is_callable()
1470 {
1471 return Err(vm.new_type_error(format!(
1472 "callable finalizer expected, got {:.50}",
1473 finalizer.class().name()
1474 )));
1475 }
1476
1477 if let Some(Some(firstiter)) = args.firstiter.as_option()
1478 && !firstiter.is_callable()
1479 {
1480 return Err(vm.new_type_error(format!(
1481 "callable firstiter expected, got {:.50}",
1482 firstiter.class().name()
1483 )));
1484 }
1485
1486 if let Some(finalizer) = args.finalizer.into_option() {
1487 *vm.async_gen_finalizer.borrow_mut() = finalizer;
1488 }
1489 if let Some(firstiter) = args.firstiter.into_option() {
1490 *vm.async_gen_firstiter.borrow_mut() = firstiter;
1491 }
1492
1493 Ok(())
1494 }
1495
1496 #[pystruct_sequence_data]
1497 pub(super) struct AsyncgenHooksData {
1498 firstiter: PyObjectRef,
1499 finalizer: PyObjectRef,
1500 }
1501
1502 #[pyattr]
1503 #[pystruct_sequence(name = "asyncgen_hooks", data = "AsyncgenHooksData")]
1504 pub(super) struct PyAsyncgenHooks;
1505
1506 #[pyclass(with(PyStructSequence))]
1507 impl PyAsyncgenHooks {}
1508
1509 #[pyfunction]
1510 fn get_asyncgen_hooks(vm: &VirtualMachine) -> AsyncgenHooksData {
1511 AsyncgenHooksData {
1512 firstiter: vm.async_gen_firstiter.borrow().clone().to_pyobject(vm),
1513 finalizer: vm.async_gen_finalizer.borrow().clone().to_pyobject(vm),
1514 }
1515 }
1516
1517 #[derive(Debug)]
1521 #[pystruct_sequence_data]
1522 pub(super) struct FlagsData {
1523 debug: u8,
1525 inspect: u8,
1527 interactive: u8,
1529 optimize: u8,
1531 dont_write_bytecode: u8,
1533 no_user_site: u8,
1535 no_site: u8,
1537 ignore_environment: u8,
1539 verbose: u8,
1541 bytes_warning: u64,
1543 quiet: u8,
1545 hash_randomization: u8,
1547 isolated: u8,
1549 dev_mode: bool,
1551 utf8_mode: u8,
1553 int_max_str_digits: i64,
1555 safe_path: bool,
1557 warn_default_encoding: u8,
1559 }
1560
1561 impl FlagsData {
1562 const fn from_settings(settings: &Settings) -> Self {
1563 Self {
1564 debug: settings.debug,
1565 inspect: settings.inspect as u8,
1566 interactive: settings.interactive as u8,
1567 optimize: settings.optimize,
1568 dont_write_bytecode: (!settings.write_bytecode) as u8,
1569 no_user_site: (!settings.user_site_directory) as u8,
1570 no_site: (!settings.import_site) as u8,
1571 ignore_environment: settings.ignore_environment as u8,
1572 verbose: settings.verbose,
1573 bytes_warning: settings.bytes_warning,
1574 quiet: settings.quiet as u8,
1575 hash_randomization: settings.hash_seed.is_none() as u8,
1576 isolated: settings.isolated as u8,
1577 dev_mode: settings.dev_mode,
1578 utf8_mode: if settings.utf8_mode < 0 {
1579 1
1580 } else {
1581 settings.utf8_mode as u8
1582 },
1583 int_max_str_digits: settings.int_max_str_digits,
1584 safe_path: settings.safe_path,
1585 warn_default_encoding: settings.warn_default_encoding as u8,
1586 }
1587 }
1588 }
1589
1590 #[pystruct_sequence(name = "flags", data = "FlagsData", no_attr)]
1591 pub(super) struct PyFlags;
1592
1593 #[pyclass(with(PyStructSequence))]
1594 impl Py<PyFlags> {
1595 #[pyslot]
1596 fn slot_new(_cls: PyTypeRef, _args: FuncArgs, vm: &VirtualMachine) -> PyResult {
1597 Err(vm.new_type_error("cannot create 'sys.flags' instances"))
1598 }
1599
1600 #[pygetset]
1601 fn context_aware_warnings(&self, vm: &VirtualMachine) -> bool {
1602 vm.state.config.settings.context_aware_warnings
1603 }
1604
1605 #[pygetset]
1606 fn thread_inherit_context(&self, vm: &VirtualMachine) -> bool {
1607 vm.state.config.settings.thread_inherit_context
1608 }
1609 }
1610
1611 #[cfg(feature = "threading")]
1612 #[pystruct_sequence_data]
1613 pub(super) struct ThreadInfoData {
1614 name: Option<&'static str>,
1615 lock: Option<&'static str>,
1616 version: Option<&'static str>,
1617 }
1618
1619 #[cfg(feature = "threading")]
1620 impl ThreadInfoData {
1621 const INFO: Self = Self {
1622 name: crate::stdlib::_thread::_thread::PYTHREAD_NAME,
1623 lock: Some("mutex+cond"),
1626 version: None,
1627 };
1628 }
1629
1630 #[cfg(feature = "threading")]
1631 #[pystruct_sequence(name = "thread_info", data = "ThreadInfoData", no_attr)]
1632 pub(super) struct PyThreadInfo;
1633
1634 #[cfg(feature = "threading")]
1635 #[pyclass(with(PyStructSequence))]
1636 impl PyThreadInfo {}
1637
1638 #[pystruct_sequence_data]
1639 pub(super) struct FloatInfoData {
1640 max: f64,
1641 max_exp: i32,
1642 max_10_exp: i32,
1643 min: f64,
1644 min_exp: i32,
1645 min_10_exp: i32,
1646 dig: u32,
1647 mant_dig: u32,
1648 epsilon: f64,
1649 radix: u32,
1650 rounds: i32,
1651 }
1652
1653 impl FloatInfoData {
1654 const INFO: Self = Self {
1655 max: f64::MAX,
1656 max_exp: f64::MAX_EXP,
1657 max_10_exp: f64::MAX_10_EXP,
1658 min: f64::MIN_POSITIVE,
1659 min_exp: f64::MIN_EXP,
1660 min_10_exp: f64::MIN_10_EXP,
1661 dig: f64::DIGITS,
1662 mant_dig: f64::MANTISSA_DIGITS,
1663 epsilon: f64::EPSILON,
1664 radix: f64::RADIX,
1665 rounds: 1, };
1667 }
1668
1669 #[pystruct_sequence(name = "float_info", data = "FloatInfoData", no_attr)]
1670 pub(super) struct PyFloatInfo;
1671
1672 #[pyclass(with(PyStructSequence))]
1673 impl PyFloatInfo {}
1674
1675 #[pystruct_sequence_data]
1676 pub(super) struct HashInfoData {
1677 width: usize,
1678 modulus: PyUHash,
1679 inf: PyHash,
1680 nan: PyHash,
1681 imag: PyHash,
1682 algorithm: &'static str,
1683 hash_bits: usize,
1684 seed_bits: usize,
1685 cutoff: usize,
1686 }
1687
1688 impl HashInfoData {
1689 const INFO: Self = {
1690 use rustpython_common::hash::*;
1691 Self {
1692 width: core::mem::size_of::<PyHash>() * 8,
1693 modulus: MODULUS,
1694 inf: INF,
1695 nan: NAN,
1696 imag: IMAG,
1697 algorithm: ALGO,
1698 hash_bits: HASH_BITS,
1699 seed_bits: SEED_BITS,
1700 cutoff: 0, }
1702 };
1703 }
1704
1705 #[pystruct_sequence(name = "hash_info", data = "HashInfoData", no_attr)]
1706 pub(super) struct PyHashInfo;
1707
1708 #[pyclass(with(PyStructSequence))]
1709 impl PyHashInfo {}
1710
1711 #[pystruct_sequence_data]
1712 pub(super) struct IntInfoData {
1713 bits_per_digit: usize,
1714 sizeof_digit: usize,
1715 default_max_str_digits: usize,
1716 str_digits_check_threshold: usize,
1717 }
1718
1719 impl IntInfoData {
1720 const INFO: Self = Self {
1721 bits_per_digit: 30, sizeof_digit: core::mem::size_of::<u32>(),
1723 default_max_str_digits: 4300,
1724 str_digits_check_threshold: 640,
1725 };
1726 }
1727
1728 #[pystruct_sequence(name = "int_info", data = "IntInfoData", no_attr)]
1729 pub(super) struct PyIntInfo;
1730
1731 #[pyclass(with(PyStructSequence))]
1732 impl PyIntInfo {}
1733
1734 #[derive(Default, Debug)]
1735 #[pystruct_sequence_data]
1736 pub struct VersionInfoData {
1737 major: usize,
1738 minor: usize,
1739 micro: usize,
1740 releaselevel: &'static str,
1741 serial: usize,
1742 }
1743
1744 impl VersionInfoData {
1745 pub const VERSION: Self = Self {
1746 major: version::MAJOR,
1747 minor: version::MINOR,
1748 micro: version::MICRO,
1749 releaselevel: version::RELEASELEVEL,
1750 serial: version::SERIAL,
1751 };
1752
1753 pub const IMPLEMENTATION: Self = Self {
1754 major: version::MAJOR_IMPL,
1755 minor: version::MINOR_IMPL,
1756 micro: version::MICRO_IMPL,
1757 releaselevel: version::RELEASELEVEL_IMPL,
1758 serial: version::SERIAL_IMPL,
1759 };
1760 }
1761
1762 #[pystruct_sequence(name = "version_info", data = "VersionInfoData", no_attr)]
1763 pub struct PyVersionInfo;
1764
1765 #[pyclass(with(PyStructSequence))]
1766 impl PyVersionInfo {
1767 #[pyslot]
1768 fn slot_new(
1769 _cls: crate::builtins::type_::PyTypeRef,
1770 _args: crate::function::FuncArgs,
1771 vm: &crate::VirtualMachine,
1772 ) -> crate::PyResult {
1773 Err(vm.new_type_error("cannot create 'sys.version_info' instances"))
1774 }
1775 }
1776
1777 #[cfg(windows)]
1778 #[derive(Default, Debug)]
1779 #[pystruct_sequence_data]
1780 pub(super) struct WindowsVersionData {
1781 major: u32,
1782 minor: u32,
1783 build: u32,
1784 platform: u32,
1785 service_pack: String,
1786 #[pystruct_sequence(skip)]
1787 service_pack_major: u16,
1788 #[pystruct_sequence(skip)]
1789 service_pack_minor: u16,
1790 #[pystruct_sequence(skip)]
1791 suite_mask: u16,
1792 #[pystruct_sequence(skip)]
1793 product_type: u8,
1794 #[pystruct_sequence(skip)]
1795 platform_version: (u32, u32, u32),
1796 }
1797
1798 #[cfg(windows)]
1799 #[pystruct_sequence(name = "getwindowsversion", data = "WindowsVersionData", no_attr)]
1800 pub(super) struct PyWindowsVersion;
1801
1802 #[cfg(windows)]
1803 #[pyclass(with(PyStructSequence))]
1804 impl PyWindowsVersion {
1805 #[pyslot]
1806 fn slot_new(_cls: PyTypeRef, _args: FuncArgs, vm: &VirtualMachine) -> PyResult {
1807 Err(vm.new_type_error("cannot create 'sys.getwindowsversion' instances"))
1808 }
1809 }
1810
1811 #[derive(Debug)]
1812 #[pystruct_sequence_data(try_from_object)]
1813 pub struct UnraisableHookArgsData {
1814 pub exc_type: PyTypeRef,
1815 pub exc_value: PyObjectRef,
1816 pub exc_traceback: PyObjectRef,
1817 pub err_msg: PyObjectRef,
1818 pub object: PyObjectRef,
1819 }
1820
1821 #[pystruct_sequence(name = "UnraisableHookArgs", data = "UnraisableHookArgsData", no_attr)]
1822 pub struct PyUnraisableHookArgs;
1823
1824 #[pyclass(with(PyStructSequence))]
1825 impl PyUnraisableHookArgs {}
1826
1827 pub(crate) fn run_audit_hooks(
1828 event: &Py<PyStr>,
1829 args: &PyObject,
1830 vm: &VirtualMachine,
1831 ) -> PyResult<()> {
1832 let hooks = vm.state.audit_hooks.lock().clone();
1833
1834 if hooks.is_empty() {
1835 return Ok(());
1836 }
1837
1838 for hook in hooks {
1839 call_audit_hook(&hook, event.to_owned().into(), args, vm)?;
1840 }
1841
1842 Ok(())
1843 }
1844
1845 fn audit_hook_can_trace(hook: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
1846 match hook.get_attr("__cantrace__", vm) {
1847 Ok(can_trace) => can_trace.try_to_bool(vm),
1848 Err(exc)
1849 if exc
1850 .class()
1851 .fast_issubclass(vm.ctx.exceptions.attribute_error) =>
1852 {
1853 Ok(false)
1854 }
1855 Err(exc) => Err(exc),
1856 }
1857 }
1858
1859 fn call_audit_hook(
1860 hook: &PyObject,
1861 event: PyObjectRef,
1862 args: &PyObject,
1863 vm: &VirtualMachine,
1864 ) -> PyResult<()> {
1865 vm.enter_tracing();
1868 let can_trace = audit_hook_can_trace(hook, vm);
1869 let result = match can_trace {
1870 Ok(can_trace) => {
1871 if can_trace {
1872 vm.leave_tracing();
1873 }
1874 let result = hook.call((event, args.to_owned()), vm).map(|_| ());
1875 if can_trace {
1876 vm.enter_tracing();
1877 }
1878 result
1879 }
1880 Err(exc) => Err(exc),
1881 };
1882
1883 vm.leave_tracing();
1884 result
1885 }
1886
1887 #[pyfunction]
1888 fn audit(event: PyStrRef, args: PosArgs, vm: &VirtualMachine) -> PyResult<()> {
1889 if vm.state.audit_hooks.lock().is_empty() {
1890 return Ok(());
1891 }
1892
1893 let args_tup: PyObjectRef = vm.ctx.new_tuple(args.into_vec()).into();
1894 run_audit_hooks(&event, &args_tup, vm)
1895 }
1896
1897 #[pyfunction]
1898 fn addaudithook(AuditHookArgs { hook }: AuditHookArgs, vm: &VirtualMachine) -> PyResult<()> {
1899 let args: PyObjectRef = vm.ctx.new_tuple(vec![]).into();
1900 let event: PyObjectRef = vm.ctx.new_str("sys.addaudithook").into();
1901
1902 let mut notified = 0;
1905 loop {
1906 let pending = {
1907 let mut hooks = vm.state.audit_hooks.lock();
1908 if hooks.len() == notified {
1909 hooks.push(hook);
1910 return Ok(());
1911 }
1912 hooks[notified..].to_vec()
1913 };
1914 for existing_hook in pending {
1915 if let Err(exc) = call_audit_hook(&existing_hook, event.clone(), &args, vm) {
1916 return if exc
1917 .class()
1918 .fast_issubclass(vm.ctx.exceptions.exception_type)
1919 {
1920 Ok(())
1921 } else {
1922 Err(exc)
1923 };
1924 }
1925 notified += 1;
1926 }
1927 }
1928 }
1929}
1930
1931fn sys_module_doc() -> Option<&'static str> {
1932 let raw = crate::function::plain_doc(sys::DOC)?;
1933 #[cfg(windows)]
1934 {
1935 Some(raw)
1936 }
1937 #[cfg(not(windows))]
1938 {
1939 use std::sync::OnceLock;
1940 static FILTERED: OnceLock<String> = OnceLock::new();
1941 let filtered = FILTERED.get_or_init(|| {
1942 let mut out = String::new();
1943 for line in raw.split_inclusive('\n') {
1944 if !line.contains("[Windows only]") {
1945 out.push_str(line);
1946 }
1947 }
1948 out
1949 });
1950 Some(filtered.as_str())
1951 }
1952}
1953
1954pub(crate) fn init_module(vm: &VirtualMachine, module: &Py<PyModule>, builtins: &Py<PyModule>) {
1955 module.__init_methods(vm).unwrap();
1956 sys::module_exec(vm, module).unwrap();
1957
1958 let modules = vm.ctx.new_dict();
1959 modules
1960 .set_item("sys", module.to_owned().into(), vm)
1961 .unwrap();
1962 modules
1963 .set_item("builtins", builtins.to_owned().into(), vm)
1964 .unwrap();
1965
1966 let jit_def = sys_jit::module_def(&vm.ctx);
1968 let jit_module = jit_def.create_module(vm).unwrap();
1969
1970 extend_module!(vm, module, {
1971 "__doc__" => sys_module_doc().to_pyobject(vm),
1972 "modules" => modules,
1973 "_jit" => jit_module,
1974 });
1975}
1976
1977pub(crate) fn set_bootstrap_stderr(vm: &VirtualMachine) -> PyResult<()> {
1978 let stderr = sys::BootstrapStderr.into_ref(&vm.ctx);
1979 let stderr_obj: crate::PyObjectRef = stderr.into();
1980 vm.sys_module.set_attr("stderr", stderr_obj.clone(), vm)?;
1981 vm.sys_module.set_attr("__stderr__", stderr_obj, vm)?;
1982 Ok(())
1983}
1984
1985pub struct PyStderr<'vm>(pub &'vm VirtualMachine);
1997
1998impl PyStderr<'_> {
1999 pub fn write_fmt(&self, args: core::fmt::Arguments<'_>) {
2000 use crate::py_io::Write;
2001
2002 let vm = self.0;
2003 if let Ok(stderr) = get_stderr(vm) {
2004 let mut stderr = crate::py_io::PyWriter(stderr, vm);
2005 if let Ok(()) = stderr.write_fmt(args) {
2006 return;
2007 }
2008 }
2009 eprint!("{args}")
2010 }
2011}
2012
2013pub fn get_stdin(vm: &VirtualMachine) -> PyResult {
2014 vm.sys_module
2015 .get_attr("stdin", vm)
2016 .map_err(|_| vm.new_runtime_error("lost sys.stdin"))
2017}
2018pub fn get_stdout(vm: &VirtualMachine) -> PyResult {
2019 vm.sys_module
2020 .get_attr("stdout", vm)
2021 .map_err(|_| vm.new_runtime_error("lost sys.stdout"))
2022}
2023pub fn get_stderr(vm: &VirtualMachine) -> PyResult {
2024 vm.sys_module
2025 .get_attr("stderr", vm)
2026 .map_err(|_| vm.new_runtime_error("lost sys.stderr"))
2027}
2028
2029pub(crate) fn sysconfigdata_name() -> String {
2030 format!(
2031 "_sysconfigdata_{}_{}_{}",
2032 sys::ABIFLAGS,
2033 sys::PLATFORM.to_string_lossy(),
2034 sys::multiarch()
2035 )
2036}