1use crate::{builtins::PyModule, convert::ToPyObject, Py, PyResult, VirtualMachine};
2
3pub(crate) use sys::{UnraisableHookArgs, __module_def, DOC, MAXSIZE, MULTIARCH};
4
5#[pymodule]
6mod sys {
7 use crate::{
8 builtins::{
9 PyBaseExceptionRef, PyDictRef, PyNamespace, PyStr, PyStrRef, PyTupleRef, PyTypeRef,
10 },
11 common::{
12 ascii,
13 hash::{PyHash, PyUHash},
14 },
15 convert::ToPyObject,
16 frame::FrameRef,
17 function::{FuncArgs, OptionalArg, PosArgs},
18 stdlib::{builtins, warnings::warn},
19 types::PyStructSequence,
20 version,
21 vm::{Settings, VirtualMachine},
22 AsObject, PyObject, PyObjectRef, PyRef, PyRefExact, PyResult,
23 };
24 use num_traits::ToPrimitive;
25 use std::{
26 env::{self, VarError},
27 path,
28 sync::atomic::Ordering,
29 };
30
31 pub(crate) const MULTIARCH: &str = env!("RUSTPYTHON_TARGET_TRIPLE");
35
36 #[pyattr(name = "_rustpython_debugbuild")]
37 const RUSTPYTHON_DEBUGBUILD: bool = cfg!(debug_assertions);
38
39 #[pyattr(name = "abiflags")]
40 pub(crate) const ABIFLAGS: &str = "";
41 #[pyattr(name = "api_version")]
42 const API_VERSION: u32 = 0x0; #[pyattr(name = "copyright")]
44 const COPYRIGHT: &str = "Copyright (c) 2019 RustPython Team";
45 #[pyattr(name = "float_repr_style")]
46 const FLOAT_REPR_STYLE: &str = "short";
47 #[pyattr(name = "_framework")]
48 const FRAMEWORK: &str = "";
49 #[pyattr(name = "hexversion")]
50 const HEXVERSION: usize = version::VERSION_HEX;
51 #[pyattr(name = "maxsize")]
52 pub(crate) const MAXSIZE: isize = isize::MAX;
53 #[pyattr(name = "maxunicode")]
54 const MAXUNICODE: u32 = std::char::MAX as u32;
55 #[pyattr(name = "platform")]
56 pub(crate) const PLATFORM: &str = {
57 cfg_if::cfg_if! {
58 if #[cfg(any(target_os = "linux", target_os = "android"))] {
59 "linux"
61 } else if #[cfg(target_os = "macos")] {
62 "darwin"
63 } else if #[cfg(windows)] {
64 "win32"
65 } else if #[cfg(target_os = "wasi")] {
66 "wasi"
67 } else {
68 "unknown"
69 }
70 }
71 };
72 #[pyattr(name = "ps1")]
73 const PS1: &str = ">>>>> ";
74 #[pyattr(name = "ps2")]
75 const PS2: &str = "..... ";
76
77 #[cfg(windows)]
78 #[pyattr(name = "_vpath")]
79 const VPATH: Option<&'static str> = None; #[cfg(windows)]
82 #[pyattr(name = "dllhandle")]
83 const DLLHANDLE: usize = 0;
84
85 #[pyattr]
86 fn default_prefix(_vm: &VirtualMachine) -> &'static str {
87 if cfg!(windows) {
89 "C:"
90 } else {
91 "/usr/local"
92 }
93 }
94 #[pyattr]
95 fn prefix(vm: &VirtualMachine) -> &'static str {
96 option_env!("RUSTPYTHON_PREFIX").unwrap_or_else(|| default_prefix(vm))
97 }
98 #[pyattr]
99 fn base_prefix(vm: &VirtualMachine) -> &'static str {
100 option_env!("RUSTPYTHON_BASEPREFIX").unwrap_or_else(|| prefix(vm))
101 }
102 #[pyattr]
103 fn exec_prefix(vm: &VirtualMachine) -> &'static str {
104 option_env!("RUSTPYTHON_BASEPREFIX").unwrap_or_else(|| prefix(vm))
105 }
106 #[pyattr]
107 fn base_exec_prefix(vm: &VirtualMachine) -> &'static str {
108 option_env!("RUSTPYTHON_BASEPREFIX").unwrap_or_else(|| exec_prefix(vm))
109 }
110 #[pyattr]
111 fn platlibdir(_vm: &VirtualMachine) -> &'static str {
112 option_env!("RUSTPYTHON_PLATLIBDIR").unwrap_or("lib")
113 }
114
115 #[pyattr]
118 fn argv(vm: &VirtualMachine) -> Vec<PyObjectRef> {
119 vm.state
120 .settings
121 .argv
122 .iter()
123 .map(|arg| vm.ctx.new_str(arg.clone()).into())
124 .collect()
125 }
126
127 #[pyattr]
128 fn builtin_module_names(vm: &VirtualMachine) -> PyTupleRef {
129 let mut module_names: Vec<_> = vm.state.module_inits.keys().cloned().collect();
130 module_names.push("sys".into());
131 module_names.push("builtins".into());
132 module_names.sort();
133 vm.ctx.new_tuple(
134 module_names
135 .into_iter()
136 .map(|n| vm.ctx.new_str(n).into())
137 .collect(),
138 )
139 }
140
141 #[pyattr]
142 fn byteorder(vm: &VirtualMachine) -> PyStrRef {
143 vm.ctx
145 .intern_str(if cfg!(target_endian = "little") {
146 "little"
147 } else if cfg!(target_endian = "big") {
148 "big"
149 } else {
150 "unknown"
151 })
152 .to_owned()
153 }
154
155 #[pyattr]
156 fn _base_executable(vm: &VirtualMachine) -> PyObjectRef {
157 let ctx = &vm.ctx;
158 if let Ok(var) = env::var("__PYVENV_LAUNCHER__") {
159 ctx.new_str(var).into()
160 } else {
161 executable(vm)
162 }
163 }
164
165 #[pyattr]
166 fn dont_write_bytecode(vm: &VirtualMachine) -> bool {
167 !vm.state.settings.write_bytecode
168 }
169
170 #[pyattr]
171 fn executable(vm: &VirtualMachine) -> PyObjectRef {
172 let ctx = &vm.ctx;
173 #[cfg(not(target_arch = "wasm32"))]
174 {
175 if let Some(exec_path) = env::args_os().next() {
176 if let Ok(path) = which::which(exec_path) {
177 return ctx
178 .new_str(
179 path.into_os_string()
180 .into_string()
181 .unwrap_or_else(|p| p.to_string_lossy().into_owned()),
182 )
183 .into();
184 }
185 }
186 }
187 if let Some(exec_path) = env::args().next() {
188 let path = path::Path::new(&exec_path);
189 if !path.exists() {
190 return ctx.new_str(ascii!("")).into();
191 }
192 if path.is_absolute() {
193 return ctx.new_str(exec_path).into();
194 }
195 if let Ok(dir) = env::current_dir() {
196 if let Ok(dir) = dir.into_os_string().into_string() {
197 return ctx
198 .new_str(format!(
199 "{}/{}",
200 dir,
201 exec_path.strip_prefix("./").unwrap_or(&exec_path)
202 ))
203 .into();
204 }
205 }
206 }
207 ctx.none()
208 }
209
210 #[pyattr]
211 fn _git(vm: &VirtualMachine) -> PyTupleRef {
212 vm.new_tuple((
213 ascii!("RustPython"),
214 version::get_git_identifier(),
215 version::get_git_revision(),
216 ))
217 }
218
219 #[pyattr]
220 fn implementation(vm: &VirtualMachine) -> PyRef<PyNamespace> {
221 let ctx = &vm.ctx;
223 py_namespace!(vm, {
224 "name" => ctx.new_str(ascii!("rustpython")),
225 "cache_tag" => ctx.new_str(ascii!("rustpython-01")),
226 "_multiarch" => ctx.new_str(MULTIARCH.to_owned()),
227 "version" => version_info(vm),
228 "hexversion" => ctx.new_int(version::VERSION_HEX),
229 })
230 }
231
232 #[pyattr]
233 fn meta_path(_vm: &VirtualMachine) -> Vec<PyObjectRef> {
234 Vec::new()
235 }
236
237 #[pyattr]
238 fn orig_argv(vm: &VirtualMachine) -> Vec<PyObjectRef> {
239 env::args().map(|arg| vm.ctx.new_str(arg).into()).collect()
240 }
241
242 #[pyattr]
243 fn path(vm: &VirtualMachine) -> Vec<PyObjectRef> {
244 vm.state
245 .settings
246 .path_list
247 .iter()
248 .map(|path| vm.ctx.new_str(path.clone()).into())
249 .collect()
250 }
251
252 #[pyattr]
253 fn path_hooks(_vm: &VirtualMachine) -> Vec<PyObjectRef> {
254 Vec::new()
255 }
256
257 #[pyattr]
258 fn path_importer_cache(vm: &VirtualMachine) -> PyDictRef {
259 vm.ctx.new_dict()
260 }
261
262 #[pyattr]
263 fn pycache_prefix(vm: &VirtualMachine) -> PyObjectRef {
264 vm.ctx.none()
265 }
266
267 #[pyattr]
268 fn version(_vm: &VirtualMachine) -> String {
269 version::get_version()
270 }
271
272 #[cfg(windows)]
273 #[pyattr]
274 fn winver(_vm: &VirtualMachine) -> String {
275 version::get_winver_number()
277 }
278
279 #[pyattr]
280 fn _xoptions(vm: &VirtualMachine) -> PyDictRef {
281 let ctx = &vm.ctx;
282 let xopts = ctx.new_dict();
283 for (key, value) in &vm.state.settings.xoptions {
284 let value = value.as_ref().map_or_else(
285 || ctx.new_bool(true).into(),
286 |s| ctx.new_str(s.clone()).into(),
287 );
288 xopts.set_item(&**key, value, vm).unwrap();
289 }
290 xopts
291 }
292
293 #[pyattr]
294 fn warnoptions(vm: &VirtualMachine) -> Vec<PyObjectRef> {
295 vm.state
296 .settings
297 .warnoptions
298 .iter()
299 .map(|s| vm.ctx.new_str(s.clone()).into())
300 .collect()
301 }
302
303 #[pyfunction]
304 fn audit(_args: FuncArgs) {
305 }
307
308 #[pyfunction]
309 fn exit(code: OptionalArg<PyObjectRef>, vm: &VirtualMachine) -> PyResult {
310 let code = code.unwrap_or_none(vm);
311 Err(vm.new_exception(vm.ctx.exceptions.system_exit.to_owned(), vec![code]))
312 }
313
314 #[pyfunction]
315 fn exception(vm: &VirtualMachine) -> Option<PyBaseExceptionRef> {
316 vm.topmost_exception()
317 }
318
319 #[pyfunction(name = "__displayhook__")]
320 #[pyfunction]
321 fn displayhook(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
322 if vm.is_none(&obj) {
324 return Ok(());
325 }
326 vm.builtins.set_attr("_", vm.ctx.none(), vm)?;
328 let repr = obj.repr(vm)?.into();
330 builtins::print(PosArgs::new(vec![repr]), Default::default(), vm)?;
331 vm.builtins.set_attr("_", obj, vm)?;
332 Ok(())
333 }
334
335 #[pyfunction(name = "__excepthook__")]
336 #[pyfunction]
337 fn excepthook(
338 exc_type: PyObjectRef,
339 exc_val: PyObjectRef,
340 exc_tb: PyObjectRef,
341 vm: &VirtualMachine,
342 ) -> PyResult<()> {
343 let exc = vm.normalize_exception(exc_type, exc_val, exc_tb)?;
344 let stderr = super::get_stderr(vm)?;
345 vm.write_exception(&mut crate::py_io::PyWriter(stderr, vm), &exc)
346 }
347
348 #[pyfunction(name = "__breakpointhook__")]
349 #[pyfunction]
350 pub fn breakpointhook(args: FuncArgs, vm: &VirtualMachine) -> PyResult {
351 let env_var = std::env::var("PYTHONBREAKPOINT")
352 .and_then(|env_var| {
353 if env_var.is_empty() {
354 Err(VarError::NotPresent)
355 } else {
356 Ok(env_var)
357 }
358 })
359 .unwrap_or_else(|_| "pdb.set_trace".to_owned());
360
361 if env_var.eq("0") {
362 return Ok(vm.ctx.none());
363 };
364
365 let print_unimportable_module_warn = || {
366 warn(
367 vm.ctx.exceptions.runtime_warning,
368 format!("Ignoring unimportable $PYTHONBREAKPOINT: \"{env_var}\"",),
369 0,
370 vm,
371 )
372 .unwrap();
373 Ok(vm.ctx.none())
374 };
375
376 let last = match env_var.rsplit_once('.') {
377 Some((_, last)) => last,
378 None if !env_var.is_empty() => env_var.as_str(),
379 _ => return print_unimportable_module_warn(),
380 };
381
382 let (module_path, attr_name) = if last == env_var {
383 ("builtins", env_var.as_str())
384 } else {
385 (&env_var[..(env_var.len() - last.len() - 1)], last)
386 };
387
388 let module = match vm.import(&vm.ctx.new_str(module_path), 0) {
389 Ok(module) => module,
390 Err(_) => {
391 return print_unimportable_module_warn();
392 }
393 };
394
395 match vm.get_attribute_opt(module, &vm.ctx.new_str(attr_name)) {
396 Ok(Some(hook)) => hook.as_ref().call(args, vm),
397 _ => print_unimportable_module_warn(),
398 }
399 }
400
401 #[pyfunction]
402 fn exc_info(vm: &VirtualMachine) -> (PyObjectRef, PyObjectRef, PyObjectRef) {
403 match vm.topmost_exception() {
404 Some(exception) => vm.split_exception(exception),
405 None => (vm.ctx.none(), vm.ctx.none(), vm.ctx.none()),
406 }
407 }
408
409 #[pyattr]
410 fn flags(vm: &VirtualMachine) -> PyTupleRef {
411 Flags::from_settings(&vm.state.settings).into_struct_sequence(vm)
412 }
413
414 #[pyattr]
415 fn float_info(vm: &VirtualMachine) -> PyTupleRef {
416 PyFloatInfo::INFO.into_struct_sequence(vm)
417 }
418
419 #[pyfunction]
420 fn getdefaultencoding() -> &'static str {
421 crate::codecs::DEFAULT_ENCODING
422 }
423
424 #[pyfunction]
425 fn getrefcount(obj: PyObjectRef) -> usize {
426 obj.strong_count()
427 }
428
429 #[pyfunction]
430 fn getrecursionlimit(vm: &VirtualMachine) -> usize {
431 vm.recursion_limit.get()
432 }
433
434 #[derive(FromArgs)]
435 struct GetsizeofArgs {
436 obj: PyObjectRef,
437 #[pyarg(any, optional)]
438 default: Option<PyObjectRef>,
439 }
440
441 #[pyfunction]
442 fn getsizeof(args: GetsizeofArgs, vm: &VirtualMachine) -> PyResult {
443 let sizeof = || -> PyResult<usize> {
444 let res = vm.call_special_method(&args.obj, identifier!(vm, __sizeof__), ())?;
445 let res = res.try_index(vm)?.try_to_primitive::<usize>(vm)?;
446 Ok(res + std::mem::size_of::<PyObject>())
447 };
448 sizeof()
449 .map(|x| vm.ctx.new_int(x).into())
450 .or_else(|err| args.default.ok_or(err))
451 }
452
453 #[pyfunction]
454 fn getfilesystemencoding(_vm: &VirtualMachine) -> String {
455 "utf-8".to_owned()
457 }
458
459 #[cfg(not(windows))]
460 #[pyfunction]
461 fn getfilesystemencodeerrors(_vm: &VirtualMachine) -> String {
462 "surrogateescape".to_owned()
463 }
464
465 #[cfg(windows)]
466 #[pyfunction]
467 fn getfilesystemencodeerrors(_vm: &VirtualMachine) -> String {
468 "surrogatepass".to_owned()
469 }
470
471 #[pyfunction]
472 fn getprofile(vm: &VirtualMachine) -> PyObjectRef {
473 vm.profile_func.borrow().clone()
474 }
475
476 #[pyfunction]
477 fn _getframe(offset: OptionalArg<usize>, vm: &VirtualMachine) -> PyResult<FrameRef> {
478 let offset = offset.into_option().unwrap_or(0);
479 if offset > vm.frames.borrow().len() - 1 {
480 return Err(vm.new_value_error("call stack is not deep enough".to_owned()));
481 }
482 let idx = vm.frames.borrow().len() - offset - 1;
483 let frame = &vm.frames.borrow()[idx];
484 Ok(frame.clone())
485 }
486
487 #[pyfunction]
488 fn gettrace(vm: &VirtualMachine) -> PyObjectRef {
489 vm.trace_func.borrow().clone()
490 }
491
492 #[cfg(windows)]
493 #[pyfunction]
494 fn getwindowsversion(vm: &VirtualMachine) -> PyResult<crate::builtins::tuple::PyTupleRef> {
495 use std::ffi::OsString;
496 use std::os::windows::ffi::OsStringExt;
497 use windows_sys::Win32::System::SystemInformation::{
498 GetVersionExW, OSVERSIONINFOEXW, OSVERSIONINFOW,
499 };
500
501 let mut version: OSVERSIONINFOEXW = unsafe { std::mem::zeroed() };
502 version.dwOSVersionInfoSize = std::mem::size_of::<OSVERSIONINFOEXW>() as u32;
503 let result = unsafe {
504 let osvi = &mut version as *mut OSVERSIONINFOEXW as *mut OSVERSIONINFOW;
505 GetVersionExW(osvi)
508 };
509
510 if result == 0 {
511 return Err(vm.new_os_error("failed to get windows version".to_owned()));
512 }
513
514 let service_pack = {
515 let (last, _) = version
516 .szCSDVersion
517 .iter()
518 .take_while(|&x| x != &0)
519 .enumerate()
520 .last()
521 .unwrap_or((0, &0));
522 let sp = OsString::from_wide(&version.szCSDVersion[..last]);
523 sp.into_string()
524 .map_err(|_| vm.new_os_error("service pack is not ASCII".to_owned()))?
525 };
526 Ok(WindowsVersion {
527 major: version.dwMajorVersion,
528 minor: version.dwMinorVersion,
529 build: version.dwBuildNumber,
530 platform: version.dwPlatformId,
531 service_pack,
532 service_pack_major: version.wServicePackMajor,
533 service_pack_minor: version.wServicePackMinor,
534 suite_mask: version.wSuiteMask,
535 product_type: version.wProductType,
536 platform_version: (
537 version.dwMajorVersion,
538 version.dwMinorVersion,
539 version.dwBuildNumber,
540 ), }
542 .into_struct_sequence(vm))
543 }
544
545 fn _unraisablehook(unraisable: UnraisableHookArgs, vm: &VirtualMachine) -> PyResult<()> {
546 use super::PyStderr;
547
548 let stderr = PyStderr(vm);
549 if !vm.is_none(&unraisable.object) {
550 if !vm.is_none(&unraisable.err_msg) {
551 write!(stderr, "{}: ", unraisable.err_msg.str(vm)?);
552 } else {
553 write!(stderr, "Exception ignored in: ");
554 }
555 let repr = &unraisable.object.repr(vm);
557 let str = match repr {
558 Ok(v) => v.to_string(),
559 Err(_) => format!(
560 "<object {} repr() failed>",
561 unraisable.object.class().name()
562 ),
563 };
564 writeln!(stderr, "{str}");
565 } else if !vm.is_none(&unraisable.err_msg) {
566 writeln!(stderr, "{}:", unraisable.err_msg.str(vm)?);
567 }
568
569 let tb_module = vm.import("traceback", 0)?;
571 let print_stack = tb_module.get_attr("print_stack", vm)?;
572 print_stack.call((), vm)?;
573
574 if vm.is_none(unraisable.exc_type.as_object()) {
575 }
577 assert!(unraisable
578 .exc_type
579 .fast_issubclass(vm.ctx.exceptions.base_exception_type));
580
581 if !vm.is_none(&unraisable.exc_value) {
584 write!(stderr, "{}: ", unraisable.exc_type);
585 if let Ok(str) = unraisable.exc_value.str(vm) {
586 write!(stderr, "{}", str.as_str());
587 } else {
588 write!(stderr, "<exception str() failed>");
589 }
590 }
591 writeln!(stderr);
592 Ok(())
595 }
596
597 #[pyattr]
598 #[pyfunction(name = "__unraisablehook__")]
599 fn unraisablehook(unraisable: UnraisableHookArgs, vm: &VirtualMachine) {
600 if let Err(e) = _unraisablehook(unraisable, vm) {
601 let stderr = super::PyStderr(vm);
602 writeln!(
603 stderr,
604 "{}",
605 e.as_object()
606 .repr(vm)
607 .unwrap_or_else(|_| vm.ctx.empty_str.to_owned())
608 .as_str()
609 );
610 }
611 }
612
613 #[pyattr]
614 fn hash_info(vm: &VirtualMachine) -> PyTupleRef {
615 PyHashInfo::INFO.into_struct_sequence(vm)
616 }
617
618 #[pyfunction]
619 fn intern(s: PyRefExact<PyStr>, vm: &VirtualMachine) -> PyRef<PyStr> {
620 vm.ctx.intern_str(s).to_owned()
621 }
622
623 #[pyattr]
624 fn int_info(vm: &VirtualMachine) -> PyTupleRef {
625 PyIntInfo::INFO.into_struct_sequence(vm)
626 }
627
628 #[pyfunction]
629 fn get_int_max_str_digits(vm: &VirtualMachine) -> usize {
630 vm.state.int_max_str_digits.load()
631 }
632
633 #[pyfunction]
634 fn set_int_max_str_digits(maxdigits: usize, vm: &VirtualMachine) -> PyResult<()> {
635 let threshold = PyIntInfo::INFO.str_digits_check_threshold;
636 if maxdigits == 0 || maxdigits >= threshold {
637 vm.state.int_max_str_digits.store(maxdigits);
638 Ok(())
639 } else {
640 let error = format!("maxdigits must be 0 or larger than {:?}", threshold);
641 Err(vm.new_value_error(error))
642 }
643 }
644
645 #[pyfunction]
646 fn is_finalizing(vm: &VirtualMachine) -> bool {
647 vm.state.finalizing.load(Ordering::Acquire)
648 }
649
650 #[pyfunction]
651 fn setprofile(profilefunc: PyObjectRef, vm: &VirtualMachine) {
652 vm.profile_func.replace(profilefunc);
653 update_use_tracing(vm);
654 }
655
656 #[pyfunction]
657 fn setrecursionlimit(recursion_limit: i32, vm: &VirtualMachine) -> PyResult<()> {
658 let recursion_limit = recursion_limit
659 .to_usize()
660 .filter(|&u| u >= 1)
661 .ok_or_else(|| {
662 vm.new_value_error(
663 "recursion limit must be greater than or equal to one".to_owned(),
664 )
665 })?;
666 let recursion_depth = vm.current_recursion_depth();
667
668 if recursion_limit > recursion_depth {
669 vm.recursion_limit.set(recursion_limit);
670 Ok(())
671 } else {
672 Err(vm.new_recursion_error(format!(
673 "cannot set the recursion limit to {recursion_limit} at the recursion depth {recursion_depth}: the limit is too low"
674 )))
675 }
676 }
677
678 #[pyfunction]
679 fn settrace(tracefunc: PyObjectRef, vm: &VirtualMachine) {
680 vm.trace_func.replace(tracefunc);
681 update_use_tracing(vm);
682 }
683
684 #[cfg(feature = "threading")]
685 #[pyattr]
686 fn thread_info(vm: &VirtualMachine) -> PyTupleRef {
687 PyThreadInfo::INFO.into_struct_sequence(vm)
688 }
689
690 #[pyattr]
691 fn version_info(vm: &VirtualMachine) -> PyTupleRef {
692 VersionInfo::VERSION.into_struct_sequence(vm)
693 }
694
695 fn update_use_tracing(vm: &VirtualMachine) {
696 let trace_is_none = vm.is_none(&vm.trace_func.borrow());
697 let profile_is_none = vm.is_none(&vm.profile_func.borrow());
698 let tracing = !(trace_is_none && profile_is_none);
699 vm.use_tracing.set(tracing);
700 }
701
702 #[pyfunction]
703 fn set_coroutine_origin_tracking_depth(depth: i32, vm: &VirtualMachine) -> PyResult<()> {
704 if depth < 0 {
705 return Err(vm.new_value_error("depth must be >= 0".to_owned()));
706 }
707 crate::vm::thread::COROUTINE_ORIGIN_TRACKING_DEPTH.with(|cell| cell.set(depth as _));
708 Ok(())
709 }
710
711 #[pyfunction]
712 fn get_coroutine_origin_tracking_depth() -> i32 {
713 crate::vm::thread::COROUTINE_ORIGIN_TRACKING_DEPTH.with(|cell| cell.get()) as _
714 }
715
716 #[derive(FromArgs)]
717 struct SetAsyncgenHooksArgs {
718 #[pyarg(any, optional)]
719 firstiter: OptionalArg<Option<PyObjectRef>>,
720 #[pyarg(any, optional)]
721 finalizer: OptionalArg<Option<PyObjectRef>>,
722 }
723
724 #[pyfunction]
725 fn set_asyncgen_hooks(args: SetAsyncgenHooksArgs, vm: &VirtualMachine) -> PyResult<()> {
726 if let Some(Some(finalizer)) = args.finalizer.as_option() {
727 if !finalizer.is_callable() {
728 return Err(vm.new_type_error(format!(
729 "callable finalizer expected, got {:.50}",
730 finalizer.class().name()
731 )));
732 }
733 }
734
735 if let Some(Some(firstiter)) = args.firstiter.as_option() {
736 if !firstiter.is_callable() {
737 return Err(vm.new_type_error(format!(
738 "callable firstiter expected, got {:.50}",
739 firstiter.class().name()
740 )));
741 }
742 }
743
744 if let Some(finalizer) = args.finalizer.into_option() {
745 crate::vm::thread::ASYNC_GEN_FINALIZER.with(|cell| {
746 cell.replace(finalizer);
747 });
748 }
749 if let Some(firstiter) = args.firstiter.into_option() {
750 crate::vm::thread::ASYNC_GEN_FIRSTITER.with(|cell| {
751 cell.replace(firstiter);
752 });
753 }
754
755 Ok(())
756 }
757
758 #[pyclass(no_attr, name = "asyncgen_hooks")]
759 #[derive(PyStructSequence)]
760 pub(super) struct PyAsyncgenHooks {
761 firstiter: PyObjectRef,
762 finalizer: PyObjectRef,
763 }
764
765 #[pyclass(with(PyStructSequence))]
766 impl PyAsyncgenHooks {}
767
768 #[pyfunction]
769 fn get_asyncgen_hooks(vm: &VirtualMachine) -> PyAsyncgenHooks {
770 PyAsyncgenHooks {
771 firstiter: crate::vm::thread::ASYNC_GEN_FIRSTITER
772 .with(|cell| cell.borrow().clone().to_pyobject(vm)),
773 finalizer: crate::vm::thread::ASYNC_GEN_FINALIZER
774 .with(|cell| cell.borrow().clone().to_pyobject(vm)),
775 }
776 }
777
778 #[pyclass(no_attr, name = "flags", module = "sys")]
782 #[derive(Debug, PyStructSequence)]
783 pub(super) struct Flags {
784 debug: u8,
786 inspect: u8,
788 interactive: u8,
790 optimize: u8,
792 dont_write_bytecode: u8,
794 no_user_site: u8,
796 no_site: u8,
798 ignore_environment: u8,
800 verbose: u8,
802 bytes_warning: u64,
804 quiet: u8,
806 hash_randomization: u8,
808 isolated: u8,
810 dev_mode: bool,
812 utf8_mode: u8,
814 int_max_str_digits: i64,
816 safe_path: bool,
818 warn_default_encoding: u8,
820 }
821
822 #[pyclass(with(PyStructSequence))]
823 impl Flags {
824 fn from_settings(settings: &Settings) -> Self {
825 Self {
826 debug: settings.debug as u8,
827 inspect: settings.inspect as u8,
828 interactive: settings.interactive as u8,
829 optimize: settings.optimize,
830 dont_write_bytecode: (!settings.write_bytecode) as u8,
831 no_user_site: (!settings.user_site_directory) as u8,
832 no_site: (!settings.import_site) as u8,
833 ignore_environment: settings.ignore_environment as u8,
834 verbose: settings.verbose,
835 bytes_warning: settings.bytes_warning,
836 quiet: settings.quiet as u8,
837 hash_randomization: settings.hash_seed.is_none() as u8,
838 isolated: settings.isolated as u8,
839 dev_mode: settings.dev_mode,
840 utf8_mode: settings.utf8_mode,
841 int_max_str_digits: settings.int_max_str_digits,
842 safe_path: settings.safe_path,
843 warn_default_encoding: settings.warn_default_encoding as u8,
844 }
845 }
846
847 #[pyslot]
848 fn slot_new(_cls: PyTypeRef, _args: FuncArgs, vm: &VirtualMachine) -> PyResult {
849 Err(vm.new_type_error("cannot create 'sys.flags' instances".to_owned()))
850 }
851 }
852
853 #[cfg(feature = "threading")]
854 #[pyclass(no_attr, name = "thread_info")]
855 #[derive(PyStructSequence)]
856 pub(super) struct PyThreadInfo {
857 name: Option<&'static str>,
858 lock: Option<&'static str>,
859 version: Option<&'static str>,
860 }
861
862 #[cfg(feature = "threading")]
863 #[pyclass(with(PyStructSequence))]
864 impl PyThreadInfo {
865 const INFO: Self = PyThreadInfo {
866 name: crate::stdlib::thread::_thread::PYTHREAD_NAME,
867 lock: Some("mutex+cond"),
870 version: None,
871 };
872 }
873
874 #[pyclass(no_attr, name = "float_info")]
875 #[derive(PyStructSequence)]
876 pub(super) struct PyFloatInfo {
877 max: f64,
878 max_exp: i32,
879 max_10_exp: i32,
880 min: f64,
881 min_exp: i32,
882 min_10_exp: i32,
883 dig: u32,
884 mant_dig: u32,
885 epsilon: f64,
886 radix: u32,
887 rounds: i32,
888 }
889
890 #[pyclass(with(PyStructSequence))]
891 impl PyFloatInfo {
892 const INFO: Self = PyFloatInfo {
893 max: f64::MAX,
894 max_exp: f64::MAX_EXP,
895 max_10_exp: f64::MAX_10_EXP,
896 min: f64::MIN_POSITIVE,
897 min_exp: f64::MIN_EXP,
898 min_10_exp: f64::MIN_10_EXP,
899 dig: f64::DIGITS,
900 mant_dig: f64::MANTISSA_DIGITS,
901 epsilon: f64::EPSILON,
902 radix: f64::RADIX,
903 rounds: 1, };
905 }
906
907 #[pyclass(no_attr, name = "hash_info")]
908 #[derive(PyStructSequence)]
909 pub(super) struct PyHashInfo {
910 width: usize,
911 modulus: PyUHash,
912 inf: PyHash,
913 nan: PyHash,
914 imag: PyHash,
915 algorithm: &'static str,
916 hash_bits: usize,
917 seed_bits: usize,
918 cutoff: usize,
919 }
920
921 #[pyclass(with(PyStructSequence))]
922 impl PyHashInfo {
923 const INFO: Self = {
924 use rustpython_common::hash::*;
925 PyHashInfo {
926 width: std::mem::size_of::<PyHash>() * 8,
927 modulus: MODULUS,
928 inf: INF,
929 nan: NAN,
930 imag: IMAG,
931 algorithm: ALGO,
932 hash_bits: HASH_BITS,
933 seed_bits: SEED_BITS,
934 cutoff: 0, }
936 };
937 }
938
939 #[pyclass(no_attr, name = "int_info")]
940 #[derive(PyStructSequence)]
941 pub(super) struct PyIntInfo {
942 bits_per_digit: usize,
943 sizeof_digit: usize,
944 default_max_str_digits: usize,
945 str_digits_check_threshold: usize,
946 }
947
948 #[pyclass(with(PyStructSequence))]
949 impl PyIntInfo {
950 const INFO: Self = PyIntInfo {
951 bits_per_digit: 30, sizeof_digit: std::mem::size_of::<u32>(),
953 default_max_str_digits: 4300,
954 str_digits_check_threshold: 640,
955 };
956 }
957
958 #[pyclass(no_attr, name = "version_info")]
959 #[derive(Default, Debug, PyStructSequence)]
960 pub struct VersionInfo {
961 major: usize,
962 minor: usize,
963 micro: usize,
964 releaselevel: &'static str,
965 serial: usize,
966 }
967
968 #[pyclass(with(PyStructSequence))]
969 impl VersionInfo {
970 pub const VERSION: VersionInfo = VersionInfo {
971 major: version::MAJOR,
972 minor: version::MINOR,
973 micro: version::MICRO,
974 releaselevel: version::RELEASELEVEL,
975 serial: version::SERIAL,
976 };
977 #[pyslot]
978 fn slot_new(
979 _cls: crate::builtins::type_::PyTypeRef,
980 _args: crate::function::FuncArgs,
981 vm: &crate::VirtualMachine,
982 ) -> crate::PyResult {
983 Err(vm.new_type_error("cannot create 'sys.version_info' instances".to_owned()))
984 }
985 }
986
987 #[cfg(windows)]
988 #[pyclass(no_attr, name = "getwindowsversion")]
989 #[derive(Default, Debug, PyStructSequence)]
990 pub(super) struct WindowsVersion {
991 major: u32,
992 minor: u32,
993 build: u32,
994 platform: u32,
995 service_pack: String,
996 service_pack_major: u16,
997 service_pack_minor: u16,
998 suite_mask: u16,
999 product_type: u8,
1000 platform_version: (u32, u32, u32),
1001 }
1002
1003 #[cfg(windows)]
1004 #[pyclass(with(PyStructSequence))]
1005 impl WindowsVersion {}
1006
1007 #[pyclass(no_attr, name = "UnraisableHookArgs")]
1008 #[derive(Debug, PyStructSequence, TryIntoPyStructSequence)]
1009 pub struct UnraisableHookArgs {
1010 pub exc_type: PyTypeRef,
1011 pub exc_value: PyObjectRef,
1012 pub exc_traceback: PyObjectRef,
1013 pub err_msg: PyObjectRef,
1014 pub object: PyObjectRef,
1015 }
1016
1017 #[pyclass(with(PyStructSequence))]
1018 impl UnraisableHookArgs {}
1019}
1020
1021pub(crate) fn init_module(vm: &VirtualMachine, module: &Py<PyModule>, builtins: &Py<PyModule>) {
1022 sys::extend_module(vm, module).unwrap();
1023
1024 let modules = vm.ctx.new_dict();
1025 modules
1026 .set_item("sys", module.to_owned().into(), vm)
1027 .unwrap();
1028 modules
1029 .set_item("builtins", builtins.to_owned().into(), vm)
1030 .unwrap();
1031 extend_module!(vm, module, {
1032 "__doc__" => sys::DOC.to_owned().to_pyobject(vm),
1033 "modules" => modules,
1034 });
1035}
1036
1037pub struct PyStderr<'vm>(pub &'vm VirtualMachine);
1049
1050impl PyStderr<'_> {
1051 pub fn write_fmt(&self, args: std::fmt::Arguments<'_>) {
1052 use crate::py_io::Write;
1053
1054 let vm = self.0;
1055 if let Ok(stderr) = get_stderr(vm) {
1056 let mut stderr = crate::py_io::PyWriter(stderr, vm);
1057 if let Ok(()) = stderr.write_fmt(args) {
1058 return;
1059 }
1060 }
1061 eprint!("{args}")
1062 }
1063}
1064
1065pub fn get_stdin(vm: &VirtualMachine) -> PyResult {
1066 vm.sys_module
1067 .get_attr("stdin", vm)
1068 .map_err(|_| vm.new_runtime_error("lost sys.stdin".to_owned()))
1069}
1070pub fn get_stdout(vm: &VirtualMachine) -> PyResult {
1071 vm.sys_module
1072 .get_attr("stdout", vm)
1073 .map_err(|_| vm.new_runtime_error("lost sys.stdout".to_owned()))
1074}
1075pub fn get_stderr(vm: &VirtualMachine) -> PyResult {
1076 vm.sys_module
1077 .get_attr("stderr", vm)
1078 .map_err(|_| vm.new_runtime_error("lost sys.stderr".to_owned()))
1079}
1080
1081pub(crate) fn sysconfigdata_name() -> String {
1082 format!(
1083 "_sysconfigdata_{}_{}_{}",
1084 sys::ABIFLAGS,
1085 sys::PLATFORM,
1086 sys::MULTIARCH
1087 )
1088}