diff --git a/Cargo.lock b/Cargo.lock index 2ac962772d..d78a65543e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -374,6 +374,11 @@ dependencies = [ "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "exitcode" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "failure" version = "0.1.5" @@ -1166,6 +1171,7 @@ dependencies = [ "crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "exitcode 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "flame 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "flamer 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "flate2 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2055,6 +2061,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" "checksum ena 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f56c93cc076508c549d9bb747f79aa9b4eb098be7b8cad8830c3137ef52d1e00" "checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" +"checksum exitcode 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193" "checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" diff --git a/Lib/os.py b/Lib/os.py index 59a55c99b7..449381eb06 100644 --- a/Lib/os.py +++ b/Lib/os.py @@ -45,6 +45,7 @@ def _get_exports_list(module): import _os from _os import * +from _os import _exit __all__.extend(_get_exports_list(_os)) del _os diff --git a/tests/snippets/exit.py b/tests/snippets/exit.py index d355102805..f6dc387322 100644 --- a/tests/snippets/exit.py +++ b/tests/snippets/exit.py @@ -2,3 +2,38 @@ with assert_raises(SystemExit): exit() + +with assert_raises(SystemExit): + exit(None) + +with assert_raises(SystemExit): + exit(1) + +with assert_raises(SystemExit): + exit("AB") + +with assert_raises(SystemExit): + quit() + +with assert_raises(SystemExit): + quit(None) + +with assert_raises(SystemExit): + quit(1) + +with assert_raises(SystemExit): + quit("AB") + +import sys + +with assert_raises(SystemExit): + sys.exit() + +with assert_raises(SystemExit): + sys.exit(None) + +with assert_raises(SystemExit): + sys.exit(1) + +with assert_raises(SystemExit): + sys.exit("AB") \ No newline at end of file diff --git a/vm/Cargo.toml b/vm/Cargo.toml index d6f872e0a5..2ded048f56 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -71,6 +71,9 @@ flamer = { version = "0.3", optional = true } [target.'cfg(all(unix, not(any(target_os = "android", target_os = "redox"))))'.dependencies] pwd = "1" +[target.'cfg(unix)'.dependencies] +exitcode = "1.1.2" + [target.'cfg(not(target_arch = "wasm32"))'.dependencies] crc32fast = "1.2.0" adler32 = "1.0.3" diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index f19753f7de..07c211b5fd 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -601,14 +601,9 @@ impl Printer for std::io::StdoutLock<'_> { } } -pub fn builtin_exit(exit_code_arg: OptionalArg, vm: &VirtualMachine) -> PyResult<()> { - if let OptionalArg::Present(exit_code_obj) = exit_code_arg { - match i32::try_from_object(&vm, exit_code_obj.clone()) { - Ok(code) => std::process::exit(code), - _ => println!("{}", vm.to_str(&exit_code_obj)?.as_str()), - } - } - std::process::exit(0); +pub fn builtin_exit(exit_code_arg: OptionalArg, vm: &VirtualMachine) -> PyResult { + let code = exit_code_arg.unwrap_or_else(|| vm.new_int(0)); + Err(vm.new_exception_obj(vm.ctx.exceptions.system_exit.clone(), vec![code])?) } pub fn builtin_print(objects: Args, options: PrintOptions, vm: &VirtualMachine) -> PyResult<()> { diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index cc19b48915..6c9a892a7b 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -11,6 +11,8 @@ use std::os::windows::fs::OpenOptionsExt; use std::time::{Duration, SystemTime}; use std::{env, fs}; +#[cfg(unix)] +use exitcode; #[cfg(unix)] use nix::errno::Errno; #[cfg(all(unix, not(target_os = "redox")))] @@ -972,6 +974,14 @@ fn os_cpu_count(vm: &VirtualMachine) -> PyObjectRef { vm.new_int(cpu_count) } +fn os_exit(code: PyIntRef, _vm: &VirtualMachine) -> PyResult<()> { + if let Some(code) = code.as_bigint().to_i32() { + std::process::exit(code) + } else { + panic!("unwrap error from code.as_bigint().to_i32() in os_exit()") + } +} + #[cfg(unix)] fn os_getppid(vm: &VirtualMachine) -> PyObjectRef { let ppid = unistd::getppid().as_raw(); @@ -1215,6 +1225,7 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef { "fspath" => ctx.new_rustfunc(os_fspath), "getpid" => ctx.new_rustfunc(os_getpid), "cpu_count" => ctx.new_rustfunc(os_cpu_count), + "_exit" => ctx.new_rustfunc(os_exit), "O_RDONLY" => ctx.new_int(libc::O_RDONLY), "O_WRONLY" => ctx.new_int(libc::O_WRONLY), @@ -1283,6 +1294,22 @@ fn extend_module_platform_specific(vm: &VirtualMachine, module: PyObjectRef) -> "SEEK_SET" => ctx.new_int(Whence::SeekSet as i8), "SEEK_CUR" => ctx.new_int(Whence::SeekCur as i8), "SEEK_END" => ctx.new_int(Whence::SeekEnd as i8), + "EX_OK" => ctx.new_int(exitcode::OK as i8), + "EX_USAGE" => ctx.new_int(exitcode::USAGE as i8), + "EX_DATAERR" => ctx.new_int(exitcode::DATAERR as i8), + "EX_NOINPUT" => ctx.new_int(exitcode::NOINPUT as i8), + "EX_NOUSER" => ctx.new_int(exitcode::NOUSER as i8), + "EX_NOHOST" => ctx.new_int(exitcode::NOHOST as i8), + "EX_UNAVAILABLE" => ctx.new_int(exitcode::UNAVAILABLE as i8), + "EX_SOFTWARE" => ctx.new_int(exitcode::SOFTWARE as i8), + "EX_OSERR" => ctx.new_int(exitcode::OSERR as i8), + "EX_OSFILE" => ctx.new_int(exitcode::OSFILE as i8), + "EX_CANTCREAT" => ctx.new_int(exitcode::CANTCREAT as i8), + "EX_IOERR" => ctx.new_int(exitcode::IOERR as i8), + "EX_TEMPFAIL" => ctx.new_int(exitcode::TEMPFAIL as i8), + "EX_PROTOCOL" => ctx.new_int(exitcode::PROTOCOL as i8), + "EX_NOPERM" => ctx.new_int(exitcode::NOPERM as i8), + "EX_CONFIG" => ctx.new_int(exitcode::CONFIG as i8), }); #[cfg(not(target_os = "redox"))]