use std::rc::Rc; use std::{env, mem}; use crate::frame::FrameRef; use crate::function::OptionalArg; use crate::obj::objstr::PyStringRef; use crate::pyobject::{ IntoPyObject, ItemProtocol, PyClassImpl, PyContext, PyObjectRef, PyResult, TypeProtocol, }; use crate::version; use crate::vm::{PySettings, VirtualMachine}; /* * The magic sys module. */ fn argv(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.new_list( vm.settings .argv .iter() .map(|arg| vm.new_str(arg.to_owned())) .collect(), ) } fn executable(ctx: &PyContext) -> PyObjectRef { if let Some(arg) = env::args().next() { ctx.new_str(arg) } else { ctx.none() } } fn getframe(offset: OptionalArg, vm: &VirtualMachine) -> PyResult { let offset = offset.into_option().unwrap_or(0); if offset > vm.frames.borrow().len() - 1 { return Err(vm.new_value_error("call stack is not deep enough".to_owned())); } let idx = vm.frames.borrow().len() - offset - 1; let frame = &vm.frames.borrow()[idx]; Ok(frame.clone()) } /// sys.flags /// /// Flags provided through command line arguments or environment vars. #[pystruct_sequence(name = "flags")] #[derive(Default, Debug)] struct SysFlags { /// -d debug: bool, /// -i inspect: bool, /// -i interactive: bool, /// -O or -OO optimize: u8, /// -B dont_write_bytecode: bool, /// -s no_user_site: bool, /// -S no_site: bool, /// -E ignore_environment: bool, /// -v verbose: u8, /// -b bytes_warning: bool, /// -q quiet: bool, /// -R hash_randomization: bool, /// -I isolated: bool, /// -X dev dev_mode: bool, /// -X utf8 utf8_mode: bool, } impl SysFlags { fn from_settings(settings: &PySettings) -> Self { // Start with sensible defaults: let mut flags: SysFlags = Default::default(); flags.debug = settings.debug; flags.inspect = settings.inspect; flags.optimize = settings.optimize; flags.no_user_site = settings.no_user_site; flags.no_site = settings.no_site; flags.ignore_environment = settings.ignore_environment; flags.verbose = settings.verbose; flags.quiet = settings.quiet; flags.dont_write_bytecode = settings.dont_write_bytecode; flags } } fn sys_getrefcount(obj: PyObjectRef) -> usize { Rc::strong_count(&obj) } fn sys_getsizeof(obj: PyObjectRef) -> usize { // TODO: implement default optional argument. mem::size_of_val(&obj) } fn sys_getfilesystemencoding(_vm: &VirtualMachine) -> String { // TODO: implement non-utf-8 mode. "utf-8".to_owned() } fn sys_getdefaultencoding(_vm: &VirtualMachine) -> String { "utf-8".to_owned() } #[cfg(not(windows))] fn sys_getfilesystemencodeerrors(_vm: &VirtualMachine) -> String { "surrogateescape".to_owned() } #[cfg(windows)] fn sys_getfilesystemencodeerrors(_vm: &VirtualMachine) -> String { "surrogatepass".to_owned() } fn sys_getprofile(vm: &VirtualMachine) -> PyObjectRef { vm.profile_func.borrow().clone() } fn sys_setprofile(profilefunc: PyObjectRef, vm: &VirtualMachine) { vm.profile_func.replace(profilefunc); update_use_tracing(vm); } fn sys_gettrace(vm: &VirtualMachine) -> PyObjectRef { vm.trace_func.borrow().clone() } fn sys_settrace(tracefunc: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { vm.trace_func.replace(tracefunc); update_use_tracing(vm); vm.ctx.none() } fn update_use_tracing(vm: &VirtualMachine) { let trace_is_none = vm.is_none(&vm.trace_func.borrow()); let profile_is_none = vm.is_none(&vm.profile_func.borrow()); let tracing = !(trace_is_none && profile_is_none); vm.use_tracing.replace(tracing); } fn sys_getrecursionlimit(vm: &VirtualMachine) -> usize { vm.recursion_limit.get() } fn sys_setrecursionlimit(recursion_limit: usize, vm: &VirtualMachine) -> PyResult { let recursion_depth = vm.frames.borrow().len(); if recursion_limit > recursion_depth + 1 { vm.recursion_limit.set(recursion_limit); Ok(vm.ctx.none()) } else { Err(vm.new_recursion_error(format!( "cannot set the recursion limit to {} at the recursion depth {}: the limit is too low", recursion_limit, recursion_depth ))) } } // TODO implement string interning, this will be key for performance fn sys_intern(value: PyStringRef) -> PyStringRef { value } fn sys_exc_info(vm: &VirtualMachine) -> PyObjectRef { let exc_info = match vm.current_exception() { Some(exception) => vec![ exception.class().into_object(), exception.clone().into_object(), exception .traceback() .map_or(vm.get_none(), |tb| tb.into_object()), ], None => vec![vm.get_none(), vm.get_none(), vm.get_none()], }; vm.ctx.new_tuple(exc_info) } fn sys_git_info(vm: &VirtualMachine) -> PyObjectRef { vm.ctx.new_tuple(vec![ vm.ctx.new_str("RustPython".to_owned()), vm.ctx.new_str(version::get_git_identifier()), vm.ctx.new_str(version::get_git_revision()), ]) } fn sys_exit(code: OptionalArg, vm: &VirtualMachine) -> PyResult { let code = code.unwrap_or_else(|| vm.new_int(0)); Err(vm.new_exception(vm.ctx.exceptions.system_exit.clone(), vec![code])) } pub fn make_module(vm: &VirtualMachine, module: PyObjectRef, builtins: PyObjectRef) { let ctx = &vm.ctx; let flags_type = SysFlags::make_class(ctx); let flags = SysFlags::from_settings(&vm.settings) .into_struct_sequence(vm, flags_type) .unwrap(); let version_info_type = version::VersionInfo::make_class(ctx); let version_info = version::get_version_info() .into_struct_sequence(vm, version_info_type) .unwrap(); // TODO Add crate version to this namespace let implementation = py_namespace!(vm, { "name" => ctx.new_str("rustpython".to_owned()), "cache_tag" => ctx.new_str("rustpython-01".to_owned()), }); let path = ctx.new_list( vm.settings .path_list .iter() .map(|path| ctx.new_str(path.clone())) .collect(), ); let platform = if cfg!(target_os = "linux") { "linux".to_owned() } else if cfg!(target_os = "macos") { "darwin".to_owned() } else if cfg!(target_os = "windows") { "win32".to_owned() } else if cfg!(target_os = "android") { // Linux as well. see https://bugs.python.org/issue32637 "linux".to_owned() } else { "unknown".to_owned() }; let framework = "".to_owned(); // https://doc.rust-lang.org/reference/conditional-compilation.html#target_endian let bytorder = if cfg!(target_endian = "little") { "little".to_owned() } else if cfg!(target_endian = "big") { "big".to_owned() } else { "unknown".to_owned() }; let copyright = "Copyright (c) 2019 RustPython Team"; let sys_doc = "This module provides access to some objects used or maintained by the interpreter and to functions that interact strongly with the interpreter. Dynamic objects: argv -- command line arguments; argv[0] is the script pathname if known path -- module search path; path[0] is the script directory, else '' modules -- dictionary of loaded modules displayhook -- called to show results in an interactive session excepthook -- called to handle any uncaught exception other than SystemExit To customize printing in an interactive session or to install a custom top-level exception handler, assign other functions to replace these. stdin -- standard input file object; used by input() stdout -- standard output file object; used by print() stderr -- standard error object; used for error messages By assigning other file objects (or objects that behave like files) to these, it is possible to redirect all of the interpreter's I/O. last_type -- type of last uncaught exception last_value -- value of last uncaught exception last_traceback -- traceback of last uncaught exception These three are only available in an interactive session after a traceback has been printed. Static objects: builtin_module_names -- tuple of module names built into this interpreter copyright -- copyright notice pertaining to this interpreter exec_prefix -- prefix used to find the machine-specific Python library executable -- absolute path of the executable binary of the Python interpreter float_info -- a struct sequence with information about the float implementation. float_repr_style -- string indicating the style of repr() output for floats hash_info -- a struct sequence with information about the hash algorithm. hexversion -- version information encoded as a single integer implementation -- Python implementation information. int_info -- a struct sequence with information about the int implementation. maxsize -- the largest supported length of containers. maxunicode -- the value of the largest Unicode code point platform -- platform identifier prefix -- prefix used to find the Python library thread_info -- a struct sequence with information about the thread implementation. version -- the version of this interpreter as a string version_info -- version information as a named tuple __stdin__ -- the original stdin; don't touch! __stdout__ -- the original stdout; don't touch! __stderr__ -- the original stderr; don't touch! __displayhook__ -- the original displayhook; don't touch! __excepthook__ -- the original excepthook; don't touch! Functions: displayhook() -- print an object to the screen, and save it in builtins._ excepthook() -- print an exception and its traceback to sys.stderr exc_info() -- return thread-safe information about the current exception exit() -- exit the interpreter by raising SystemExit getdlopenflags() -- returns flags to be used for dlopen() calls getprofile() -- get the global profiling function getrefcount() -- return the reference count for an object (plus one :-) getrecursionlimit() -- return the max recursion depth for the interpreter getsizeof() -- return the size of an object in bytes gettrace() -- get the global debug tracing function setcheckinterval() -- control how often the interpreter checks for events setdlopenflags() -- set the flags to be used for dlopen() calls setprofile() -- set the global profiling function setrecursionlimit() -- set the max recursion depth for the interpreter settrace() -- set the global debug tracing function "; let mut module_names: Vec = vm.stdlib_inits.borrow().keys().cloned().collect(); module_names.push("sys".to_owned()); module_names.push("builtins".to_owned()); module_names.sort(); let builtin_module_names = ctx.new_tuple( module_names .iter() .map(|v| v.into_pyobject(vm).unwrap()) .collect(), ); let modules = ctx.new_dict(); let prefix = option_env!("RUSTPYTHON_PREFIX").unwrap_or("/usr/local"); let base_prefix = option_env!("RUSTPYTHON_BASEPREFIX").unwrap_or(prefix); let exec_prefix = option_env!("RUSTPYTHON_EXECPREFIX").unwrap_or(prefix); let base_exec_prefix = option_env!("RUSTPYTHON_BASEEXECPREFIX").unwrap_or(exec_prefix); extend_module!(vm, module, { "__name__" => ctx.new_str(String::from("sys")), "argv" => argv(vm), "builtin_module_names" => builtin_module_names, "byteorder" => ctx.new_str(bytorder), "copyright" => ctx.new_str(copyright.to_owned()), "executable" => executable(ctx), "flags" => flags, "getrefcount" => ctx.new_function(sys_getrefcount), "getrecursionlimit" => ctx.new_function(sys_getrecursionlimit), "getsizeof" => ctx.new_function(sys_getsizeof), "implementation" => implementation, "getfilesystemencoding" => ctx.new_function(sys_getfilesystemencoding), "getfilesystemencodeerrors" => ctx.new_function(sys_getfilesystemencodeerrors), "getdefaultencoding" => ctx.new_function(sys_getdefaultencoding), "getprofile" => ctx.new_function(sys_getprofile), "gettrace" => ctx.new_function(sys_gettrace), "intern" => ctx.new_function(sys_intern), "maxunicode" => ctx.new_int(0x0010_FFFF), "maxsize" => ctx.new_int(std::isize::MAX), "path" => path, "ps1" => ctx.new_str(">>>>> ".to_owned()), "ps2" => ctx.new_str("..... ".to_owned()), "__doc__" => ctx.new_str(sys_doc.to_owned()), "_getframe" => ctx.new_function(getframe), "modules" => modules.clone(), "warnoptions" => ctx.new_list(vec![]), "platform" => ctx.new_str(platform), "_framework" => ctx.new_str(framework), "meta_path" => ctx.new_list(vec![]), "path_hooks" => ctx.new_list(vec![]), "path_importer_cache" => ctx.new_dict(), "pycache_prefix" => vm.get_none(), "dont_write_bytecode" => vm.new_bool(vm.settings.dont_write_bytecode), "setprofile" => ctx.new_function(sys_setprofile), "setrecursionlimit" => ctx.new_function(sys_setrecursionlimit), "settrace" => ctx.new_function(sys_settrace), "version" => vm.new_str(version::get_version()), "version_info" => version_info, "_git" => sys_git_info(vm), "exc_info" => ctx.new_function(sys_exc_info), "prefix" => ctx.new_str(prefix.to_owned()), "base_prefix" => ctx.new_str(base_prefix.to_owned()), "exec_prefix" => ctx.new_str(exec_prefix.to_owned()), "base_exec_prefix" => ctx.new_str(base_exec_prefix.to_owned()), "exit" => ctx.new_function(sys_exit), "abiflags" => ctx.new_str("".to_owned()), }); modules.set_item("sys", module.clone(), vm).unwrap(); modules.set_item("builtins", builtins.clone(), vm).unwrap(); }