diff --git a/crates/vm/src/stdlib/builtins.rs b/crates/vm/src/stdlib/builtins.rs index fd35b287211..9afd4cf761c 100644 --- a/crates/vm/src/stdlib/builtins.rs +++ b/crates/vm/src/stdlib/builtins.rs @@ -268,12 +268,10 @@ mod builtins { let mode_str = args.mode.as_str(); let optimize: i32 = args.optimize.map_or(-1, |v| v.value); - let optimize: u8 = if optimize == -1 { - vm.state.config.settings.optimize - } else { - optimize - .try_into() - .map_err(|_| vm.new_value_error("compile() optimize value invalid"))? + let optimize: u8 = match optimize { + -1 => vm.state.config.settings.optimize, + 0..=2 => optimize as u8, + _ => return Err(vm.new_value_error("compile(): invalid optimize value")), }; if args @@ -348,7 +346,7 @@ mod builtins { let flags: i32 = args.flags.map_or(0, |v| v.value); if !(flags & !_ast::PY_COMPILE_FLAGS_MASK).is_zero() { - return Err(vm.new_value_error("compile() unrecognized flags")); + return Err(vm.new_value_error("compile(): unrecognised flags")); } let allow_incomplete = !(flags & _ast::PY_CF_ALLOW_INCOMPLETE_INPUT).is_zero(); diff --git a/extra_tests/snippets/builtin_compile.py b/extra_tests/snippets/builtin_compile.py new file mode 100644 index 00000000000..15095c0eede --- /dev/null +++ b/extra_tests/snippets/builtin_compile.py @@ -0,0 +1,46 @@ +from testutils import assert_raises + +# compile() basic mode acceptance +assert isinstance( + compile("x = 1", "", "exec"), type(compile("", "", "exec")) +) +assert compile("1 + 1", "", "eval") is not None +assert compile("1", "", "single") is not None + +# `optimize` accepts -1 (use config default), 0, 1, 2 only. +# Anything else raises ValueError with CPython's exact wording. +for ok in (-1, 0, 1, 2): + compile("x = 1", "", "exec", optimize=ok) + + +def _check_optimize_error(value): + try: + compile("x = 1", "", "exec", optimize=value) + except ValueError as e: + assert str(e) == "compile(): invalid optimize value", repr(e) + else: + raise AssertionError(f"expected ValueError for optimize={value!r}") + + +for bad in (3, 4, 99, 255, 256, 1000, -2, -99, -128): + _check_optimize_error(bad) + +# Huge `optimize` values raise OverflowError during argument conversion, +# not ValueError. The exact wording differs from CPython here (Rust i32 +# vs C int) — checking the type only, matching test_compile.py. +assert_raises(OverflowError, compile, "x = 1", "", "exec", optimize=1 << 1000) + + +# Unrecognised `flags` bits raise ValueError. CPython uses British spelling +# ("unrecognised") so the message must match exactly. +def _check_flags_error(flags): + try: + compile("x = 1", "", "exec", flags=flags) + except ValueError as e: + assert str(e) == "compile(): unrecognised flags", repr(e) + else: + raise AssertionError(f"expected ValueError for flags={flags!r}") + + +_check_flags_error(99999) +_check_flags_error(0x10000)