mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
Compare commits
9 Commits
2025-06-02
...
2025-06-16
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c968fe0fd9 | ||
|
|
125f14190a | ||
|
|
a6dd2d805b | ||
|
|
6723bf30a7 | ||
|
|
2c61a12bed | ||
|
|
f560b4cbfb | ||
|
|
4e094eaa55 | ||
|
|
2e368baf2a | ||
|
|
323ea3b96b |
7
.github/workflows/ci.yaml
vendored
7
.github/workflows/ci.yaml
vendored
@@ -32,11 +32,6 @@ env:
|
||||
test_pathlib
|
||||
test_posixpath
|
||||
test_venv
|
||||
# configparser: https://github.com/RustPython/RustPython/issues/4995#issuecomment-1582397417
|
||||
# socketserver: seems related to configparser crash.
|
||||
MACOS_SKIPS: >-
|
||||
test_configparser
|
||||
test_socketserver
|
||||
# PLATFORM_INDEPENDENT_TESTS are tests that do not depend on the underlying OS. They are currently
|
||||
# only run on Linux to speed up the CI.
|
||||
PLATFORM_INDEPENDENT_TESTS: >-
|
||||
@@ -284,7 +279,7 @@ jobs:
|
||||
run: target/release/rustpython -m test -j 1 -u all --slowest --fail-env-changed -v -x ${{ env.PLATFORM_INDEPENDENT_TESTS }}
|
||||
- if: runner.os == 'macOS'
|
||||
name: run cpython platform-dependent tests (MacOS)
|
||||
run: target/release/rustpython -m test -j 1 --slowest --fail-env-changed -v -x ${{ env.PLATFORM_INDEPENDENT_TESTS }} ${{ env.MACOS_SKIPS }}
|
||||
run: target/release/rustpython -m test -j 1 --slowest --fail-env-changed -v -x ${{ env.PLATFORM_INDEPENDENT_TESTS }}
|
||||
- if: runner.os == 'Windows'
|
||||
name: run cpython platform-dependent tests (windows partial - fixme)
|
||||
run:
|
||||
|
||||
16
Cargo.lock
generated
16
Cargo.lock
generated
@@ -365,18 +365,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.37"
|
||||
version = "4.5.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071"
|
||||
checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.37"
|
||||
version = "4.5.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2"
|
||||
checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
@@ -1302,9 +1302,9 @@ checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
||||
|
||||
[[package]]
|
||||
name = "libffi"
|
||||
version = "4.0.0"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a9434b6fc77375fb624698d5f8c49d7e80b10d59eb1219afda27d1f824d4074"
|
||||
checksum = "ebfd30a67b482a08116e753d0656cb626548cf4242543e5cc005be7639d99838"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"libffi-sys",
|
||||
@@ -1312,9 +1312,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libffi-sys"
|
||||
version = "3.2.0"
|
||||
version = "3.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ead36a2496acfc8edd6cc32352110e9478ac5b9b5f5b9856ebd3d28019addb84"
|
||||
checksum = "f003aa318c9f0ee69eb0ada7c78f5c9d2fedd2ceb274173b5c7ff475eee584a3"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
10
Lib/codeop.py
vendored
10
Lib/codeop.py
vendored
@@ -65,14 +65,10 @@ def _maybe_compile(compiler, source, filename, symbol):
|
||||
try:
|
||||
compiler(source + "\n", filename, symbol)
|
||||
return None
|
||||
except _IncompleteInputError as e:
|
||||
return None
|
||||
except SyntaxError as e:
|
||||
# XXX: RustPython; support multiline definitions in REPL
|
||||
# See also: https://github.com/RustPython/RustPython/pull/5743
|
||||
strerr = str(e)
|
||||
if source.endswith(":") and "expected an indented block" in strerr:
|
||||
return None
|
||||
elif "incomplete input" in str(e):
|
||||
return None
|
||||
pass
|
||||
# fallthrough
|
||||
|
||||
return compiler(source, filename, symbol, incomplete_input=False)
|
||||
|
||||
2
Lib/test/test_baseexception.py
vendored
2
Lib/test/test_baseexception.py
vendored
@@ -83,6 +83,8 @@ class ExceptionClassTests(unittest.TestCase):
|
||||
exc_set = set(e for e in exc_set if not e.startswith('_'))
|
||||
# RUSTPYTHON specific
|
||||
exc_set.discard("JitError")
|
||||
# TODO: RUSTPYTHON; this will be officially introduced in Python 3.15
|
||||
exc_set.discard("IncompleteInputError")
|
||||
self.assertEqual(len(exc_set), 0, "%s not accounted for" % exc_set)
|
||||
|
||||
interface_tests = ("length", "args", "str", "repr")
|
||||
|
||||
4
Lib/test/test_dataclasses.py
vendored
4
Lib/test/test_dataclasses.py
vendored
@@ -2088,8 +2088,6 @@ class TestDocString(unittest.TestCase):
|
||||
|
||||
self.assertDocStrEqual(C.__doc__, "C(x:List[int]=<factory>)")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_docstring_deque_field(self):
|
||||
@dataclass
|
||||
class C:
|
||||
@@ -2097,8 +2095,6 @@ class TestDocString(unittest.TestCase):
|
||||
|
||||
self.assertDocStrEqual(C.__doc__, "C(x:collections.deque)")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_docstring_deque_field_with_default_factory(self):
|
||||
@dataclass
|
||||
class C:
|
||||
|
||||
2
Lib/test/test_httplib.py
vendored
2
Lib/test/test_httplib.py
vendored
@@ -1,3 +1,4 @@
|
||||
import sys
|
||||
import errno
|
||||
from http import client, HTTPStatus
|
||||
import io
|
||||
@@ -1781,6 +1782,7 @@ class HTTPSTest(TestCase):
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.skipIf(sys.platform == 'darwin', 'Occasionally success on macOS')
|
||||
def test_local_unknown_cert(self):
|
||||
# The custom cert isn't known to the default trust bundle
|
||||
import ssl
|
||||
|
||||
3
Lib/test/test_pickle.py
vendored
3
Lib/test/test_pickle.py
vendored
@@ -664,6 +664,9 @@ class CompatPickleTests(unittest.TestCase):
|
||||
BaseExceptionGroup,
|
||||
ExceptionGroup):
|
||||
continue
|
||||
# TODO: RUSTPYTHON: fix name mapping for _IncompleteInputError
|
||||
if exc is _IncompleteInputError:
|
||||
continue
|
||||
if exc is not OSError and issubclass(exc, OSError):
|
||||
self.assertEqual(reverse_mapping('builtins', name),
|
||||
('exceptions', 'OSError'))
|
||||
|
||||
2
Lib/test/test_reprlib.py
vendored
2
Lib/test/test_reprlib.py
vendored
@@ -82,8 +82,6 @@ class ReprTests(unittest.TestCase):
|
||||
expected = repr(t3)[:-2] + "+++)"
|
||||
eq(r3.repr(t3), expected)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_container(self):
|
||||
from array import array
|
||||
from collections import deque
|
||||
|
||||
@@ -226,7 +226,7 @@ To enhance CPython compatibility, try to increase unittest coverage by checking
|
||||
Another approach is to checkout the source code: builtin functions and object
|
||||
methods are often the simplest and easiest way to contribute.
|
||||
|
||||
You can also simply run `uv run python -I whats_left.py` to assist in finding any unimplemented
|
||||
You can also simply run `python -I whats_left.py` to assist in finding any unimplemented
|
||||
method.
|
||||
|
||||
## Compiling to WebAssembly
|
||||
|
||||
@@ -151,7 +151,7 @@ pub fn compile_program(
|
||||
let mut compiler = Compiler::new(opts, source_code, "<module>".to_owned());
|
||||
compiler.compile_program(ast, symbol_table)?;
|
||||
let code = compiler.pop_code_object();
|
||||
trace!("Compilation completed: {:?}", code);
|
||||
trace!("Compilation completed: {code:?}");
|
||||
Ok(code)
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ pub fn compile_program_single(
|
||||
let mut compiler = Compiler::new(opts, source_code, "<module>".to_owned());
|
||||
compiler.compile_program_single(&ast.body, symbol_table)?;
|
||||
let code = compiler.pop_code_object();
|
||||
trace!("Compilation completed: {:?}", code);
|
||||
trace!("Compilation completed: {code:?}");
|
||||
Ok(code)
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ pub fn compile_block_expression(
|
||||
let mut compiler = Compiler::new(opts, source_code, "<module>".to_owned());
|
||||
compiler.compile_block_expr(&ast.body, symbol_table)?;
|
||||
let code = compiler.pop_code_object();
|
||||
trace!("Compilation completed: {:?}", code);
|
||||
trace!("Compilation completed: {code:?}");
|
||||
Ok(code)
|
||||
}
|
||||
|
||||
@@ -233,7 +233,7 @@ fn eprint_location(zelf: &Compiler<'_>) {
|
||||
fn unwrap_internal<T>(zelf: &Compiler<'_>, r: InternalResult<T>) -> T {
|
||||
if let Err(ref r_err) = r {
|
||||
eprintln!("=== CODEGEN PANIC INFO ===");
|
||||
eprintln!("This IS an internal error: {}", r_err);
|
||||
eprintln!("This IS an internal error: {r_err}");
|
||||
eprint_location(zelf);
|
||||
eprintln!("=== END PANIC INFO ===");
|
||||
}
|
||||
@@ -671,7 +671,7 @@ impl Compiler<'_> {
|
||||
|
||||
fn compile_statement(&mut self, statement: &Stmt) -> CompileResult<()> {
|
||||
use ruff_python_ast::*;
|
||||
trace!("Compiling {:?}", statement);
|
||||
trace!("Compiling {statement:?}");
|
||||
self.set_source_range(statement.range());
|
||||
|
||||
match &statement {
|
||||
@@ -1907,7 +1907,7 @@ impl Compiler<'_> {
|
||||
|
||||
fn compile_error_forbidden_name(&mut self, name: &str) -> CodegenError {
|
||||
// TODO: make into error (fine for now since it realistically errors out earlier)
|
||||
panic!("Failing due to forbidden name {:?}", name);
|
||||
panic!("Failing due to forbidden name {name:?}");
|
||||
}
|
||||
|
||||
/// Ensures that `pc.fail_pop` has at least `n + 1` entries.
|
||||
@@ -3209,7 +3209,7 @@ impl Compiler<'_> {
|
||||
|
||||
fn compile_expression(&mut self, expression: &Expr) -> CompileResult<()> {
|
||||
use ruff_python_ast::*;
|
||||
trace!("Compiling {:?}", expression);
|
||||
trace!("Compiling {expression:?}");
|
||||
let range = expression.range();
|
||||
self.set_source_range(range);
|
||||
|
||||
@@ -4432,7 +4432,7 @@ pub fn ruff_int_to_bigint(int: &Int) -> Result<BigInt, CodegenErrorType> {
|
||||
fn parse_big_integer(int: &Int) -> Result<BigInt, CodegenErrorType> {
|
||||
// TODO: Improve ruff API
|
||||
// Can we avoid this copy?
|
||||
let s = format!("{}", int);
|
||||
let s = format!("{int}");
|
||||
let mut s = s.as_str();
|
||||
// See: https://peps.python.org/pep-0515/#literal-grammar
|
||||
let radix = match s.get(0..2) {
|
||||
|
||||
@@ -1388,12 +1388,12 @@ impl Instruction {
|
||||
let value = ctx.get_constant(idx.get(arg) as usize);
|
||||
match value.borrow_constant() {
|
||||
BorrowedConstant::Code { code } if expand_code_objects => {
|
||||
write!(f, "{:pad$}({:?}):", op, code)?;
|
||||
write!(f, "{op:pad$}({code:?}):")?;
|
||||
code.display_inner(f, true, level + 1)?;
|
||||
Ok(())
|
||||
}
|
||||
c => {
|
||||
write!(f, "{:pad$}(", op)?;
|
||||
write!(f, "{op:pad$}(")?;
|
||||
c.fmt_display(f)?;
|
||||
write!(f, ")")
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ pub enum CompileErrorType {
|
||||
pub struct ParseError {
|
||||
#[source]
|
||||
pub error: parser::ParseErrorType,
|
||||
pub raw_location: ruff_text_size::TextRange,
|
||||
pub location: SourceLocation,
|
||||
pub source_path: String,
|
||||
}
|
||||
@@ -48,6 +49,7 @@ impl CompileError {
|
||||
let location = source_code.source_location(error.location.start());
|
||||
Self::Parse(ParseError {
|
||||
error: error.error,
|
||||
raw_location: error.location,
|
||||
location,
|
||||
source_path: source_code.path.to_owned(),
|
||||
})
|
||||
|
||||
@@ -197,12 +197,12 @@ fn run_rustpython(vm: &VirtualMachine, run_mode: RunMode) -> PyResult<()> {
|
||||
}
|
||||
let res = match run_mode {
|
||||
RunMode::Command(command) => {
|
||||
debug!("Running command {}", command);
|
||||
debug!("Running command {command}");
|
||||
vm.run_code_string(scope.clone(), &command, "<stdin>".to_owned())
|
||||
.map(drop)
|
||||
}
|
||||
RunMode::Module(module) => {
|
||||
debug!("Running module {}", module);
|
||||
debug!("Running module {module}");
|
||||
vm.run_module(&module)
|
||||
}
|
||||
RunMode::InstallPip(installer) => install_pip(installer, scope.clone(), vm),
|
||||
|
||||
103
src/shell.rs
103
src/shell.rs
@@ -1,7 +1,8 @@
|
||||
mod helper;
|
||||
|
||||
use rustpython_compiler::{
|
||||
CompileError, ParseError, parser::LexicalErrorType, parser::ParseErrorType,
|
||||
CompileError, ParseError, parser::FStringErrorType, parser::LexicalErrorType,
|
||||
parser::ParseErrorType,
|
||||
};
|
||||
use rustpython_vm::{
|
||||
AsObject, PyResult, VirtualMachine,
|
||||
@@ -14,7 +15,8 @@ use rustpython_vm::{
|
||||
enum ShellExecResult {
|
||||
Ok,
|
||||
PyErr(PyBaseExceptionRef),
|
||||
Continue,
|
||||
ContinueBlock,
|
||||
ContinueLine,
|
||||
}
|
||||
|
||||
fn shell_exec(
|
||||
@@ -22,11 +24,17 @@ fn shell_exec(
|
||||
source: &str,
|
||||
scope: Scope,
|
||||
empty_line_given: bool,
|
||||
continuing: bool,
|
||||
continuing_block: bool,
|
||||
) -> ShellExecResult {
|
||||
// compiling expects only UNIX style line endings, and will replace windows line endings
|
||||
// internally. Since we might need to analyze the source to determine if an error could be
|
||||
// resolved by future input, we need the location from the error to match the source code that
|
||||
// was actually compiled.
|
||||
#[cfg(windows)]
|
||||
let source = &source.replace("\r\n", "\n");
|
||||
match vm.compile(source, compiler::Mode::Single, "<stdin>".to_owned()) {
|
||||
Ok(code) => {
|
||||
if empty_line_given || !continuing {
|
||||
if empty_line_given || !continuing_block {
|
||||
// We want to execute the full code
|
||||
match vm.run_code_obj(code, scope) {
|
||||
Ok(_val) => ShellExecResult::Ok,
|
||||
@@ -40,8 +48,32 @@ fn shell_exec(
|
||||
Err(CompileError::Parse(ParseError {
|
||||
error: ParseErrorType::Lexical(LexicalErrorType::Eof),
|
||||
..
|
||||
})) => ShellExecResult::Continue,
|
||||
})) => ShellExecResult::ContinueLine,
|
||||
Err(CompileError::Parse(ParseError {
|
||||
error:
|
||||
ParseErrorType::Lexical(LexicalErrorType::FStringError(
|
||||
FStringErrorType::UnterminatedTripleQuotedString,
|
||||
)),
|
||||
..
|
||||
})) => ShellExecResult::ContinueLine,
|
||||
Err(err) => {
|
||||
// Check if the error is from an unclosed triple quoted string (which should always
|
||||
// continue)
|
||||
if let CompileError::Parse(ParseError {
|
||||
error: ParseErrorType::Lexical(LexicalErrorType::UnclosedStringError),
|
||||
raw_location,
|
||||
..
|
||||
}) = err
|
||||
{
|
||||
let loc = raw_location.start().to_usize();
|
||||
let mut iter = source.chars();
|
||||
if let Some(quote) = iter.nth(loc) {
|
||||
if iter.next() == Some(quote) && iter.next() == Some(quote) {
|
||||
return ShellExecResult::ContinueLine;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// bad_error == true if we are handling an error that should be thrown even if we are continuing
|
||||
// if its an indentation error, set to true if we are continuing and the error is on column 0,
|
||||
// since indentations errors on columns other than 0 should be ignored.
|
||||
@@ -50,10 +82,12 @@ fn shell_exec(
|
||||
let bad_error = match err {
|
||||
CompileError::Parse(ref p) => {
|
||||
match &p.error {
|
||||
ParseErrorType::Lexical(LexicalErrorType::IndentationError) => continuing, // && p.location.is_some()
|
||||
ParseErrorType::Lexical(LexicalErrorType::IndentationError) => {
|
||||
continuing_block
|
||||
} // && p.location.is_some()
|
||||
ParseErrorType::OtherError(msg) => {
|
||||
if msg.starts_with("Expected an indented block") {
|
||||
continuing
|
||||
continuing_block
|
||||
} else {
|
||||
true
|
||||
}
|
||||
@@ -68,7 +102,7 @@ fn shell_exec(
|
||||
if empty_line_given || bad_error {
|
||||
ShellExecResult::PyErr(vm.new_syntax_error(&err, Some(source)))
|
||||
} else {
|
||||
ShellExecResult::Continue
|
||||
ShellExecResult::ContinueBlock
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,10 +127,19 @@ pub fn run_shell(vm: &VirtualMachine, scope: Scope) -> PyResult<()> {
|
||||
println!("No previous history.");
|
||||
}
|
||||
|
||||
let mut continuing = false;
|
||||
// We might either be waiting to know if a block is complete, or waiting to know if a multiline
|
||||
// statement is complete. In the former case, we need to ensure that we read one extra new line
|
||||
// to know that the block is complete. In the latter, we can execute as soon as the statement is
|
||||
// valid.
|
||||
let mut continuing_block = false;
|
||||
let mut continuing_line = false;
|
||||
|
||||
loop {
|
||||
let prompt_name = if continuing { "ps2" } else { "ps1" };
|
||||
let prompt_name = if continuing_block || continuing_line {
|
||||
"ps2"
|
||||
} else {
|
||||
"ps1"
|
||||
};
|
||||
let prompt = vm
|
||||
.sys_module
|
||||
.get_attr(prompt_name, vm)
|
||||
@@ -105,9 +148,12 @@ pub fn run_shell(vm: &VirtualMachine, scope: Scope) -> PyResult<()> {
|
||||
Ok(ref s) => s.as_str(),
|
||||
Err(_) => "",
|
||||
};
|
||||
|
||||
continuing_line = false;
|
||||
let result = match repl.readline(prompt) {
|
||||
ReadlineResult::Line(line) => {
|
||||
debug!("You entered {:?}", line);
|
||||
#[cfg(debug_assertions)]
|
||||
debug!("You entered {line:?}");
|
||||
|
||||
repl.add_history_entry(line.trim_end()).unwrap();
|
||||
|
||||
@@ -120,39 +166,44 @@ pub fn run_shell(vm: &VirtualMachine, scope: Scope) -> PyResult<()> {
|
||||
}
|
||||
full_input.push('\n');
|
||||
|
||||
match shell_exec(vm, &full_input, scope.clone(), empty_line_given, continuing) {
|
||||
match shell_exec(
|
||||
vm,
|
||||
&full_input,
|
||||
scope.clone(),
|
||||
empty_line_given,
|
||||
continuing_block,
|
||||
) {
|
||||
ShellExecResult::Ok => {
|
||||
if continuing {
|
||||
if continuing_block {
|
||||
if empty_line_given {
|
||||
// We should be exiting continue mode
|
||||
continuing = false;
|
||||
// We should exit continue mode since the block successfully executed
|
||||
continuing_block = false;
|
||||
full_input.clear();
|
||||
Ok(())
|
||||
} else {
|
||||
// We should stay in continue mode
|
||||
continuing = true;
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
// We aren't in continue mode so proceed normally
|
||||
continuing = false;
|
||||
full_input.clear();
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
ShellExecResult::Continue => {
|
||||
continuing = true;
|
||||
// Continue, but don't change the mode
|
||||
ShellExecResult::ContinueLine => {
|
||||
continuing_line = true;
|
||||
Ok(())
|
||||
}
|
||||
ShellExecResult::ContinueBlock => {
|
||||
continuing_block = true;
|
||||
Ok(())
|
||||
}
|
||||
ShellExecResult::PyErr(err) => {
|
||||
continuing = false;
|
||||
continuing_block = false;
|
||||
full_input.clear();
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
ReadlineResult::Interrupt => {
|
||||
continuing = false;
|
||||
continuing_block = false;
|
||||
full_input.clear();
|
||||
let keyboard_interrupt =
|
||||
vm.new_exception_empty(vm.ctx.exceptions.keyboard_interrupt.to_owned());
|
||||
|
||||
@@ -756,8 +756,7 @@ impl ToPyException for Base64DecodeError {
|
||||
InvalidLastSymbol(_, PAD) => "Excess data after padding".to_owned(),
|
||||
InvalidLastSymbol(length, _) => {
|
||||
format!(
|
||||
"Invalid base64-encoded string: number of data characters {} cannot be 1 more than a multiple of 4",
|
||||
length
|
||||
"Invalid base64-encoded string: number of data characters {length} cannot be 1 more than a multiple of 4"
|
||||
)
|
||||
}
|
||||
// TODO: clean up errors
|
||||
|
||||
@@ -346,13 +346,9 @@ mod _csv {
|
||||
if !rest.args.is_empty() {
|
||||
let arg_len = rest.args.len();
|
||||
if arg_len != 1 {
|
||||
return Err(vm.new_type_error(
|
||||
format!(
|
||||
"field_size_limit() takes at most 1 argument ({} given)",
|
||||
arg_len
|
||||
)
|
||||
.to_string(),
|
||||
));
|
||||
return Err(vm.new_type_error(format!(
|
||||
"field_size_limit() takes at most 1 argument ({arg_len} given)"
|
||||
)));
|
||||
}
|
||||
let Ok(new_size) = rest.args.first().unwrap().try_int(vm) else {
|
||||
return Err(vm.new_type_error("limit must be an integer".to_string()));
|
||||
@@ -701,7 +697,7 @@ mod _csv {
|
||||
if let Some(dialect) = g.get(name) {
|
||||
Ok(self.update_py_dialect(*dialect))
|
||||
} else {
|
||||
Err(new_csv_error(vm, format!("{} is not registered.", name)))
|
||||
Err(new_csv_error(vm, format!("{name} is not registered.")))
|
||||
}
|
||||
// TODO
|
||||
// Maybe need to update the obj from HashMap
|
||||
|
||||
@@ -363,7 +363,7 @@ impl FormatSpec {
|
||||
// Loop over all opcodes:
|
||||
for code in &self.codes {
|
||||
buffer = &mut buffer[code.pre_padding..];
|
||||
debug!("code: {:?}", code);
|
||||
debug!("code: {code:?}");
|
||||
match code.code {
|
||||
FormatType::Str => {
|
||||
let (buf, rest) = buffer.split_at_mut(code.repeat);
|
||||
@@ -407,7 +407,7 @@ impl FormatSpec {
|
||||
let mut items = Vec::with_capacity(self.arg_count);
|
||||
for code in &self.codes {
|
||||
data = &data[code.pre_padding..];
|
||||
debug!("unpack code: {:?}", code);
|
||||
debug!("unpack code: {code:?}");
|
||||
match code.code {
|
||||
FormatType::Pad => {
|
||||
data = &data[code.repeat..];
|
||||
|
||||
@@ -66,10 +66,9 @@ impl PyObjectRef {
|
||||
warnings::warn(
|
||||
vm.ctx.exceptions.deprecation_warning,
|
||||
format!(
|
||||
"__complex__ returned non-complex (type {}). \
|
||||
"__complex__ returned non-complex (type {ret_class}). \
|
||||
The ability to return an instance of a strict subclass of complex \
|
||||
is deprecated, and may be removed in a future version of Python.",
|
||||
ret_class
|
||||
is deprecated, and may be removed in a future version of Python."
|
||||
),
|
||||
1,
|
||||
vm,
|
||||
|
||||
@@ -53,14 +53,12 @@ impl Constructor for PyBaseObject {
|
||||
0 => {}
|
||||
1 => {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"class {} without an implementation for abstract method '{}'",
|
||||
name, methods
|
||||
"class {name} without an implementation for abstract method '{methods}'"
|
||||
)));
|
||||
}
|
||||
2.. => {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"class {} without an implementation for abstract methods '{}'",
|
||||
name, methods
|
||||
"class {name} without an implementation for abstract methods '{methods}'"
|
||||
)));
|
||||
}
|
||||
// TODO: remove `allow` when redox build doesn't complain about it
|
||||
|
||||
@@ -132,8 +132,7 @@ impl PyProperty {
|
||||
let func_args_len = func_args.args.len();
|
||||
let (_owner, name): (PyObjectRef, PyObjectRef) = func_args.bind(vm).map_err(|_e| {
|
||||
vm.new_type_error(format!(
|
||||
"__set_name__() takes 2 positional arguments but {} were given",
|
||||
func_args_len
|
||||
"__set_name__() takes 2 positional arguments but {func_args_len} were given"
|
||||
))
|
||||
})?;
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// cspell:ignore cmeth
|
||||
/*! Python `super` class.
|
||||
|
||||
See also [CPython source code.](https://github.com/python/cpython/blob/50b48572d9a90c5bb36e2bef6179548ea927a35a/Objects/typeobject.c#L7663)
|
||||
@@ -125,8 +126,8 @@ impl Initializer for PySuper {
|
||||
(typ, obj)
|
||||
};
|
||||
|
||||
let mut inner = PySuperInner::new(typ, obj, vm)?;
|
||||
std::mem::swap(&mut inner, &mut zelf.inner.write());
|
||||
let inner = PySuperInner::new(typ, obj, vm)?;
|
||||
*zelf.inner.write() = inner;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -419,7 +419,7 @@ impl StandardEncoding {
|
||||
match encoding {
|
||||
"be" => Some(Self::Utf32Be),
|
||||
"le" => Some(Self::Utf32Le),
|
||||
_ => return None,
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
@@ -1116,7 +1116,7 @@ fn replace_errors(err: PyObjectRef, vm: &VirtualMachine) -> PyResult<(PyObjectRe
|
||||
let replace = replacement_char.repeat(range.end - range.start);
|
||||
Ok((replace.to_pyobject(vm), range.end))
|
||||
} else {
|
||||
return Err(bad_err_type(err, vm));
|
||||
Err(bad_err_type(err, vm))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,3 +49,10 @@ impl crate::convert::ToPyException for (CompileError, Option<&str>) {
|
||||
vm.new_syntax_error(&self.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "parser", feature = "codegen"))]
|
||||
impl crate::convert::ToPyException for (CompileError, Option<&str>, bool) {
|
||||
fn to_pyexception(&self, vm: &crate::VirtualMachine) -> crate::builtins::PyBaseExceptionRef {
|
||||
vm.new_syntax_error_maybe_incomplete(&self.0, self.1, self.2)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::{PyResult, VirtualMachine, compiler, scope::Scope};
|
||||
pub fn eval(vm: &VirtualMachine, source: &str, scope: Scope, source_path: &str) -> PyResult {
|
||||
match vm.compile(source, compiler::Mode::Eval, source_path.to_owned()) {
|
||||
Ok(bytecode) => {
|
||||
debug!("Code object: {:?}", bytecode);
|
||||
debug!("Code object: {bytecode:?}");
|
||||
vm.run_code_obj(bytecode, scope)
|
||||
}
|
||||
Err(err) => Err(vm.new_syntax_error(&err, Some(source))),
|
||||
|
||||
@@ -206,7 +206,7 @@ impl VirtualMachine {
|
||||
lineno
|
||||
)?;
|
||||
} else if let Some(filename) = maybe_filename {
|
||||
filename_suffix = format!(" ({})", filename);
|
||||
filename_suffix = format!(" ({filename})");
|
||||
}
|
||||
|
||||
if let Some(text) = maybe_text {
|
||||
@@ -215,7 +215,7 @@ impl VirtualMachine {
|
||||
let l_text = r_text.trim_start_matches([' ', '\n', '\x0c']); // \x0c is \f
|
||||
let spaces = (r_text.len() - l_text.len()) as isize;
|
||||
|
||||
writeln!(output, " {}", l_text)?;
|
||||
writeln!(output, " {l_text}")?;
|
||||
|
||||
let maybe_offset: Option<isize> =
|
||||
getattr("offset").and_then(|obj| obj.try_to_value::<isize>(vm).ok());
|
||||
@@ -495,6 +495,7 @@ pub struct ExceptionZoo {
|
||||
pub not_implemented_error: &'static Py<PyType>,
|
||||
pub recursion_error: &'static Py<PyType>,
|
||||
pub syntax_error: &'static Py<PyType>,
|
||||
pub incomplete_input_error: &'static Py<PyType>,
|
||||
pub indentation_error: &'static Py<PyType>,
|
||||
pub tab_error: &'static Py<PyType>,
|
||||
pub system_error: &'static Py<PyType>,
|
||||
@@ -743,6 +744,7 @@ impl ExceptionZoo {
|
||||
let recursion_error = PyRecursionError::init_builtin_type();
|
||||
|
||||
let syntax_error = PySyntaxError::init_builtin_type();
|
||||
let incomplete_input_error = PyIncompleteInputError::init_builtin_type();
|
||||
let indentation_error = PyIndentationError::init_builtin_type();
|
||||
let tab_error = PyTabError::init_builtin_type();
|
||||
|
||||
@@ -817,6 +819,7 @@ impl ExceptionZoo {
|
||||
not_implemented_error,
|
||||
recursion_error,
|
||||
syntax_error,
|
||||
incomplete_input_error,
|
||||
indentation_error,
|
||||
tab_error,
|
||||
system_error,
|
||||
@@ -965,6 +968,7 @@ impl ExceptionZoo {
|
||||
"end_offset" => ctx.none(),
|
||||
"text" => ctx.none(),
|
||||
});
|
||||
extend_exception!(PyIncompleteInputError, ctx, excs.incomplete_input_error);
|
||||
extend_exception!(PyIndentationError, ctx, excs.indentation_error);
|
||||
extend_exception!(PyTabError, ctx, excs.tab_error);
|
||||
|
||||
@@ -1611,7 +1615,7 @@ pub(super) mod types {
|
||||
format!("{} ({}, line {})", msg, basename(filename.as_str()), lineno)
|
||||
}
|
||||
(Some(lineno), None) => {
|
||||
format!("{} (line {})", msg, lineno)
|
||||
format!("{msg} (line {lineno})")
|
||||
}
|
||||
(None, Some(filename)) => {
|
||||
format!("{} ({})", msg, basename(filename.as_str()))
|
||||
@@ -1623,6 +1627,28 @@ pub(super) mod types {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyexception(
|
||||
name = "_IncompleteInputError",
|
||||
base = "PySyntaxError",
|
||||
ctx = "incomplete_input_error"
|
||||
)]
|
||||
#[derive(Debug)]
|
||||
pub struct PyIncompleteInputError {}
|
||||
|
||||
#[pyexception]
|
||||
impl PyIncompleteInputError {
|
||||
#[pyslot]
|
||||
#[pymethod(name = "__init__")]
|
||||
pub(crate) fn slot_init(
|
||||
zelf: PyObjectRef,
|
||||
_args: FuncArgs,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
zelf.set_attr("name", vm.ctx.new_str("SyntaxError"), vm)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[pyexception(name, base = "PySyntaxError", ctx = "indentation_error", impl)]
|
||||
#[derive(Debug)]
|
||||
pub struct PyIndentationError {}
|
||||
|
||||
@@ -1667,7 +1667,8 @@ impl ExecutingFrame<'_> {
|
||||
.topmost_exception()
|
||||
.ok_or_else(|| vm.new_runtime_error("No active exception to reraise".to_owned()))?,
|
||||
};
|
||||
debug!("Exception raised: {:?} with cause: {:?}", exception, cause);
|
||||
#[cfg(debug_assertions)]
|
||||
debug!("Exception raised: {exception:?} with cause: {cause:?}");
|
||||
if let Some(cause) = cause {
|
||||
exception.set_cause(cause);
|
||||
}
|
||||
|
||||
@@ -51,8 +51,7 @@ impl PyObject {
|
||||
Err(err) => return err,
|
||||
};
|
||||
vm.new_value_error(format!(
|
||||
"invalid literal for int() with base {}: {}",
|
||||
base, repr,
|
||||
"invalid literal for int() with base {base}: {repr}",
|
||||
))
|
||||
})?;
|
||||
Ok(PyInt::from(i).into_ref(&vm.ctx))
|
||||
@@ -475,10 +474,9 @@ impl PyNumber<'_> {
|
||||
warnings::warn(
|
||||
vm.ctx.exceptions.deprecation_warning,
|
||||
format!(
|
||||
"__int__ returned non-int (type {}). \
|
||||
"__int__ returned non-int (type {ret_class}). \
|
||||
The ability to return an instance of a strict subclass of int \
|
||||
is deprecated, and may be removed in a future version of Python.",
|
||||
ret_class
|
||||
is deprecated, and may be removed in a future version of Python."
|
||||
),
|
||||
1,
|
||||
vm,
|
||||
@@ -509,10 +507,9 @@ impl PyNumber<'_> {
|
||||
warnings::warn(
|
||||
vm.ctx.exceptions.deprecation_warning,
|
||||
format!(
|
||||
"__index__ returned non-int (type {}). \
|
||||
"__index__ returned non-int (type {ret_class}). \
|
||||
The ability to return an instance of a strict subclass of int \
|
||||
is deprecated, and may be removed in a future version of Python.",
|
||||
ret_class
|
||||
is deprecated, and may be removed in a future version of Python."
|
||||
),
|
||||
1,
|
||||
vm,
|
||||
@@ -543,10 +540,9 @@ impl PyNumber<'_> {
|
||||
warnings::warn(
|
||||
vm.ctx.exceptions.deprecation_warning,
|
||||
format!(
|
||||
"__float__ returned non-float (type {}). \
|
||||
"__float__ returned non-float (type {ret_class}). \
|
||||
The ability to return an instance of a strict subclass of float \
|
||||
is deprecated, and may be removed in a future version of Python.",
|
||||
ret_class
|
||||
is deprecated, and may be removed in a future version of Python."
|
||||
),
|
||||
1,
|
||||
vm,
|
||||
|
||||
@@ -245,6 +245,7 @@ pub(crate) fn parse(
|
||||
let top = parser::parse(source, mode.into())
|
||||
.map_err(|parse_error| ParseError {
|
||||
error: parse_error.error,
|
||||
raw_location: parse_error.location,
|
||||
location: text_range_to_source_range(&source_code, parse_error.location)
|
||||
.start
|
||||
.to_source_location(),
|
||||
@@ -295,8 +296,8 @@ pub const PY_COMPILE_FLAG_AST_ONLY: i32 = 0x0400;
|
||||
// The following flags match the values from Include/cpython/compile.h
|
||||
// Caveat emptor: These flags are undocumented on purpose and depending
|
||||
// on their effect outside the standard library is **unsupported**.
|
||||
const PY_CF_DONT_IMPLY_DEDENT: i32 = 0x200;
|
||||
const PY_CF_ALLOW_INCOMPLETE_INPUT: i32 = 0x4000;
|
||||
pub const PY_CF_DONT_IMPLY_DEDENT: i32 = 0x200;
|
||||
pub const PY_CF_ALLOW_INCOMPLETE_INPUT: i32 = 0x4000;
|
||||
|
||||
// __future__ flags - sync with Lib/__future__.py
|
||||
// TODO: These flags aren't being used in rust code
|
||||
|
||||
@@ -186,6 +186,8 @@ mod builtins {
|
||||
return Err(vm.new_value_error("compile() unrecognized flags".to_owned()));
|
||||
}
|
||||
|
||||
let allow_incomplete = !(flags & ast::PY_CF_ALLOW_INCOMPLETE_INPUT).is_zero();
|
||||
|
||||
if (flags & ast::PY_COMPILE_FLAG_AST_ONLY).is_zero() {
|
||||
#[cfg(not(feature = "compiler"))]
|
||||
{
|
||||
@@ -207,14 +209,17 @@ mod builtins {
|
||||
args.filename.to_string_lossy().into_owned(),
|
||||
opts,
|
||||
)
|
||||
.map_err(|err| (err, Some(source)).to_pyexception(vm))?;
|
||||
.map_err(|err| {
|
||||
(err, Some(source), allow_incomplete).to_pyexception(vm)
|
||||
})?;
|
||||
Ok(code.into())
|
||||
}
|
||||
} else {
|
||||
let mode = mode_str
|
||||
.parse::<parser::Mode>()
|
||||
.map_err(|err| vm.new_value_error(err.to_string()))?;
|
||||
ast::parse(vm, source, mode).map_err(|e| (e, Some(source)).to_pyexception(vm))
|
||||
ast::parse(vm, source, mode)
|
||||
.map_err(|e| (e, Some(source), allow_incomplete).to_pyexception(vm))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1056,6 +1061,7 @@ pub fn init_module(vm: &VirtualMachine, module: &Py<PyModule>) {
|
||||
"NotImplementedError" => ctx.exceptions.not_implemented_error.to_owned(),
|
||||
"RecursionError" => ctx.exceptions.recursion_error.to_owned(),
|
||||
"SyntaxError" => ctx.exceptions.syntax_error.to_owned(),
|
||||
"_IncompleteInputError" => ctx.exceptions.incomplete_input_error.to_owned(),
|
||||
"IndentationError" => ctx.exceptions.indentation_error.to_owned(),
|
||||
"TabError" => ctx.exceptions.tab_error.to_owned(),
|
||||
"SystemError" => ctx.exceptions.system_error.to_owned(),
|
||||
|
||||
@@ -27,7 +27,7 @@ mod _collections {
|
||||
use std::collections::VecDeque;
|
||||
|
||||
#[pyattr]
|
||||
#[pyclass(name = "deque", unhashable = true)]
|
||||
#[pyclass(module = "collections", name = "deque", unhashable = true)]
|
||||
#[derive(Debug, Default, PyPayload)]
|
||||
struct PyDeque {
|
||||
deque: PyRwLock<VecDeque<PyObjectRef>>,
|
||||
|
||||
@@ -251,7 +251,7 @@ impl PyCSimple {
|
||||
#[pyclassmethod]
|
||||
fn repeat(cls: PyTypeRef, n: isize, vm: &VirtualMachine) -> PyResult {
|
||||
if n < 0 {
|
||||
return Err(vm.new_value_error(format!("Array length must be >= 0, not {}", n)));
|
||||
return Err(vm.new_value_error(format!("Array length must be >= 0, not {n}")));
|
||||
}
|
||||
Ok(PyCArrayType {
|
||||
inner: PyCArray {
|
||||
|
||||
@@ -13,6 +13,7 @@ use libffi::middle::{Arg, Cif, CodePtr, Type};
|
||||
use libloading::Symbol;
|
||||
use num_traits::ToPrimitive;
|
||||
use rustpython_common::lock::PyRwLock;
|
||||
use std::ffi::CString;
|
||||
use std::fmt::Debug;
|
||||
|
||||
// https://github.com/python/cpython/blob/4f8bb3947cfbc20f970ff9d9531e1132a9e95396/Modules/_ctypes/callproc.c#L15
|
||||
@@ -77,10 +78,11 @@ impl Function {
|
||||
}
|
||||
})
|
||||
.collect::<PyResult<Vec<Type>>>()?;
|
||||
let terminated = format!("{}\0", function);
|
||||
let c_function_name = CString::new(function)
|
||||
.map_err(|_| vm.new_value_error("Function name contains null bytes".to_string()))?;
|
||||
let pointer: Symbol<'_, FP> = unsafe {
|
||||
library
|
||||
.get(terminated.as_bytes())
|
||||
.get(c_function_name.as_bytes())
|
||||
.map_err(|err| err.to_string())
|
||||
.map_err(|err| vm.new_attribute_error(err))?
|
||||
};
|
||||
|
||||
@@ -53,7 +53,7 @@ impl GetAttr for PyCStructure {
|
||||
let data = zelf.data.read();
|
||||
match data.get(&name) {
|
||||
Some(value) => Ok(value.clone()),
|
||||
None => Err(vm.new_attribute_error(format!("No attribute named {}", name))),
|
||||
None => Err(vm.new_attribute_error(format!("No attribute named {name}"))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1175,7 +1175,7 @@ mod _io {
|
||||
vm.call_method(self.raw.as_ref().unwrap(), "readinto", (mem_obj.clone(),));
|
||||
|
||||
mem_obj.release();
|
||||
std::mem::swap(v, &mut read_buf.take());
|
||||
*v = read_buf.take();
|
||||
|
||||
res?
|
||||
}
|
||||
|
||||
@@ -1989,7 +1989,7 @@ pub mod module {
|
||||
let pathname = vm.ctx.new_dict();
|
||||
for variant in PathconfVar::iter() {
|
||||
// get the name of variant as a string to use as the dictionary key
|
||||
let key = vm.ctx.new_str(format!("{:?}", variant));
|
||||
let key = vm.ctx.new_str(format!("{variant:?}"));
|
||||
// get the enum from the string and convert it to an integer for the dictionary value
|
||||
let value = vm.ctx.new_int(variant as u8);
|
||||
pathname
|
||||
@@ -2185,7 +2185,7 @@ pub mod module {
|
||||
let names = vm.ctx.new_dict();
|
||||
for variant in SysconfVar::iter() {
|
||||
// get the name of variant as a string to use as the dictionary key
|
||||
let key = vm.ctx.new_str(format!("{:?}", variant));
|
||||
let key = vm.ctx.new_str(format!("{variant:?}"));
|
||||
// get the enum from the string and convert it to an integer for the dictionary value
|
||||
let value = vm.ctx.new_int(variant as u8);
|
||||
names
|
||||
|
||||
@@ -317,9 +317,9 @@ mod sys {
|
||||
let mut source = String::new();
|
||||
handle
|
||||
.read_to_string(&mut source)
|
||||
.map_err(|e| vm.new_os_error(format!("Error reading from stdin: {}", e)))?;
|
||||
.map_err(|e| vm.new_os_error(format!("Error reading from stdin: {e}")))?;
|
||||
vm.compile(&source, crate::compiler::Mode::Single, "<stdin>".to_owned())
|
||||
.map_err(|e| vm.new_os_error(format!("Error running stdin: {}", e)))?;
|
||||
.map_err(|e| vm.new_os_error(format!("Error running stdin: {e}")))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -723,7 +723,7 @@ mod sys {
|
||||
vm.state.int_max_str_digits.store(maxdigits);
|
||||
Ok(())
|
||||
} else {
|
||||
let error = format!("maxdigits must be 0 or larger than {:?}", threshold);
|
||||
let error = format!("maxdigits must be 0 or larger than {threshold:?}");
|
||||
Err(vm.new_value_error(error))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ impl VirtualMachine {
|
||||
self.run_code_string(scope, &source, path.to_owned())?;
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Failed reading file '{}': {}", path, err);
|
||||
error!("Failed reading file '{path}': {err}");
|
||||
// TODO: Need to change to ExitCode or Termination
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
@@ -320,10 +320,11 @@ impl VirtualMachine {
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "parser", feature = "compiler"))]
|
||||
pub fn new_syntax_error(
|
||||
pub fn new_syntax_error_maybe_incomplete(
|
||||
&self,
|
||||
error: &crate::compiler::CompileError,
|
||||
source: Option<&str>,
|
||||
allow_incomplete: bool,
|
||||
) -> PyBaseExceptionRef {
|
||||
use crate::source::SourceLocation;
|
||||
|
||||
@@ -343,12 +344,102 @@ impl VirtualMachine {
|
||||
..
|
||||
}) => self.ctx.exceptions.indentation_error,
|
||||
#[cfg(feature = "parser")]
|
||||
crate::compiler::CompileError::Parse(rustpython_compiler::ParseError {
|
||||
error:
|
||||
ruff_python_parser::ParseErrorType::Lexical(
|
||||
ruff_python_parser::LexicalErrorType::Eof,
|
||||
),
|
||||
..
|
||||
}) => {
|
||||
if allow_incomplete {
|
||||
self.ctx.exceptions.incomplete_input_error
|
||||
} else {
|
||||
self.ctx.exceptions.syntax_error
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "parser")]
|
||||
crate::compiler::CompileError::Parse(rustpython_compiler::ParseError {
|
||||
error:
|
||||
ruff_python_parser::ParseErrorType::Lexical(
|
||||
ruff_python_parser::LexicalErrorType::FStringError(
|
||||
ruff_python_parser::FStringErrorType::UnterminatedTripleQuotedString,
|
||||
),
|
||||
),
|
||||
..
|
||||
}) => {
|
||||
if allow_incomplete {
|
||||
self.ctx.exceptions.incomplete_input_error
|
||||
} else {
|
||||
self.ctx.exceptions.syntax_error
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "parser")]
|
||||
crate::compiler::CompileError::Parse(rustpython_compiler::ParseError {
|
||||
error:
|
||||
ruff_python_parser::ParseErrorType::Lexical(
|
||||
ruff_python_parser::LexicalErrorType::UnclosedStringError,
|
||||
),
|
||||
raw_location,
|
||||
..
|
||||
}) => {
|
||||
if allow_incomplete {
|
||||
let mut is_incomplete = false;
|
||||
|
||||
if let Some(source) = source {
|
||||
let loc = raw_location.start().to_usize();
|
||||
let mut iter = source.chars();
|
||||
if let Some(quote) = iter.nth(loc) {
|
||||
if iter.next() == Some(quote) && iter.next() == Some(quote) {
|
||||
is_incomplete = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if is_incomplete {
|
||||
self.ctx.exceptions.incomplete_input_error
|
||||
} else {
|
||||
self.ctx.exceptions.syntax_error
|
||||
}
|
||||
} else {
|
||||
self.ctx.exceptions.syntax_error
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "parser")]
|
||||
crate::compiler::CompileError::Parse(rustpython_compiler::ParseError {
|
||||
error: ruff_python_parser::ParseErrorType::OtherError(s),
|
||||
raw_location,
|
||||
..
|
||||
}) => {
|
||||
if s.starts_with("Expected an indented block after") {
|
||||
self.ctx.exceptions.indentation_error
|
||||
if allow_incomplete {
|
||||
// Check that all chars in the error are whitespace, if so, the source is
|
||||
// incomplete. Otherwise, we've found code that might violates
|
||||
// indentation rules.
|
||||
let mut is_incomplete = true;
|
||||
if let Some(source) = source {
|
||||
let start = raw_location.start().to_usize();
|
||||
let end = raw_location.end().to_usize();
|
||||
let mut iter = source.chars();
|
||||
iter.nth(start);
|
||||
for _ in start..end {
|
||||
if let Some(c) = iter.next() {
|
||||
if !c.is_ascii_whitespace() {
|
||||
is_incomplete = false;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if is_incomplete {
|
||||
self.ctx.exceptions.incomplete_input_error
|
||||
} else {
|
||||
self.ctx.exceptions.indentation_error
|
||||
}
|
||||
} else {
|
||||
self.ctx.exceptions.indentation_error
|
||||
}
|
||||
} else {
|
||||
self.ctx.exceptions.syntax_error
|
||||
}
|
||||
@@ -410,6 +501,15 @@ impl VirtualMachine {
|
||||
syntax_error
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "parser", feature = "compiler"))]
|
||||
pub fn new_syntax_error(
|
||||
&self,
|
||||
error: &crate::compiler::CompileError,
|
||||
source: Option<&str>,
|
||||
) -> PyBaseExceptionRef {
|
||||
self.new_syntax_error_maybe_incomplete(error, source, false)
|
||||
}
|
||||
|
||||
pub fn new_import_error(&self, msg: String, name: PyStrRef) -> PyBaseExceptionRef {
|
||||
let import_error = self.ctx.exceptions.import_error.to_owned();
|
||||
let exc = self.new_exception_msg(import_error, msg);
|
||||
|
||||
22
wasm/demo/package-lock.json
generated
22
wasm/demo/package-lock.json
generated
@@ -24,7 +24,7 @@
|
||||
"serve": "^14.2.4",
|
||||
"webpack": "^5.97.1",
|
||||
"webpack-cli": "^6.0.1",
|
||||
"webpack-dev-server": "^5.2.0"
|
||||
"webpack-dev-server": "^5.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/autocomplete": {
|
||||
@@ -5360,15 +5360,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-dev-server": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.0.tgz",
|
||||
"integrity": "sha512-90SqqYXA2SK36KcT6o1bvwvZfJFcmoamqeJY7+boioffX9g9C0wjjJRGUrQIuh43pb0ttX7+ssavmj/WN2RHtA==",
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.1.tgz",
|
||||
"integrity": "sha512-ml/0HIj9NLpVKOMq+SuBPLHcmbG+TGIjXRHsYfZwocUBIqEvws8NnS/V9AFQ5FKP+tgn5adwVwRrTEpGL33QFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/bonjour": "^3.5.13",
|
||||
"@types/connect-history-api-fallback": "^1.5.4",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/express-serve-static-core": "^4.17.21",
|
||||
"@types/serve-index": "^1.9.4",
|
||||
"@types/serve-static": "^1.15.5",
|
||||
"@types/sockjs": "^0.3.36",
|
||||
@@ -5416,6 +5417,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-dev-server/node_modules/@types/express-serve-static-core": {
|
||||
"version": "4.19.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz",
|
||||
"integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"@types/qs": "*",
|
||||
"@types/range-parser": "*",
|
||||
"@types/send": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-merge": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz",
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"serve": "^14.2.4",
|
||||
"webpack": "^5.97.1",
|
||||
"webpack-cli": "^6.0.1",
|
||||
"webpack-dev-server": "^5.2.0"
|
||||
"webpack-dev-server": "^5.2.1"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "webpack serve",
|
||||
|
||||
@@ -40,7 +40,7 @@ if implementation != "CPython":
|
||||
sys.exit(f"whats_left.py must be run under CPython, got {implementation} instead")
|
||||
if sys.version_info[:2] < (3, 13):
|
||||
sys.exit(
|
||||
f"whats_left.py must be run under CPython 3.13 or newer, got {implementation} {sys.version} instead"
|
||||
f"whats_left.py must be run under CPython 3.13 or newer, got {implementation} {sys.version} instead. If you have uv, try `uv run python -I whats_left.py` to select a proper Python interpreter easier."
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -676,7 +676,7 @@ impl fmt::Debug for Wtf8 {
|
||||
write_str_escaped(formatter, unsafe {
|
||||
str::from_utf8_unchecked(&self.bytes[pos..surrogate_pos])
|
||||
})?;
|
||||
write!(formatter, "\\u{{{:x}}}", surrogate)?;
|
||||
write!(formatter, "\\u{{{surrogate:x}}}")?;
|
||||
pos = surrogate_pos + 3;
|
||||
}
|
||||
write_str_escaped(formatter, unsafe {
|
||||
|
||||
Reference in New Issue
Block a user