mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Merge pull request #1785 from RustPython/coolreader18/builtin-input
Implement input()
This commit is contained in:
15 files changed
+102
-76
No files matched your search
Generated
+2
@@ -1420,6 +1420,7 @@ dependencies = [
|
||||
name = "rustpython"
|
||||
version = "0.1.1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"clap",
|
||||
"cpython",
|
||||
"dirs 2.0.2",
|
||||
@@ -1539,6 +1540,7 @@ dependencies = [
|
||||
"rustpython-compiler",
|
||||
"rustpython-derive",
|
||||
"rustpython-parser",
|
||||
"rustyline",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha-1",
|
||||
|
||||
@@ -27,6 +27,7 @@ rustpython-parser = {path = "parser", version = "0.1.1"}
|
||||
rustpython-vm = {path = "vm", version = "0.1.1"}
|
||||
dirs = "2.0"
|
||||
num-traits = "0.2.8"
|
||||
cfg-if = "0.1"
|
||||
|
||||
flame = { version = "0.2", optional = true }
|
||||
flamescope = { version = "0.1", optional = true }
|
||||
|
||||
+3
-6
@@ -1,9 +1,8 @@
|
||||
mod readline;
|
||||
#[cfg(not(target_os = "wasi"))]
|
||||
mod rustyline_helper;
|
||||
mod helper;
|
||||
|
||||
use rustpython_compiler::{compile, error::CompileError, error::CompileErrorType};
|
||||
use rustpython_parser::error::ParseErrorType;
|
||||
use rustpython_vm::readline::{Readline, ReadlineResult};
|
||||
use rustpython_vm::{
|
||||
exceptions::{print_exception, PyBaseExceptionRef},
|
||||
obj::objtype,
|
||||
@@ -12,8 +11,6 @@ use rustpython_vm::{
|
||||
VirtualMachine,
|
||||
};
|
||||
|
||||
use readline::{Readline, ReadlineResult};
|
||||
|
||||
enum ShellExecResult {
|
||||
Ok,
|
||||
PyErr(PyBaseExceptionRef),
|
||||
@@ -49,7 +46,7 @@ pub fn run_shell(vm: &VirtualMachine, scope: Scope) -> PyResult<()> {
|
||||
crate_version!()
|
||||
);
|
||||
|
||||
let mut repl = Readline::new(vm, scope.clone());
|
||||
let mut repl = Readline::new(helper::ShellHelper::new(vm, scope.clone()));
|
||||
let mut full_input = String::new();
|
||||
|
||||
// Retrieve a `history_path_str` dependent on the OS
|
||||
|
||||
@@ -2,10 +2,6 @@ use rustpython_vm::obj::objstr::PyStringRef;
|
||||
use rustpython_vm::pyobject::{PyIterable, PyResult, TryFromObject};
|
||||
use rustpython_vm::scope::{NameProtocol, Scope};
|
||||
use rustpython_vm::VirtualMachine;
|
||||
use rustyline::{
|
||||
completion::Completer, highlight::Highlighter, hint::Hinter, validate::Validator, Context,
|
||||
Helper,
|
||||
};
|
||||
|
||||
pub struct ShellHelper<'vm> {
|
||||
vm: &'vm VirtualMachine,
|
||||
@@ -146,24 +142,32 @@ impl<'vm> ShellHelper<'vm> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Completer for ShellHelper<'_> {
|
||||
type Candidate = String;
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(not(target_os = "wasi"))] {
|
||||
use rustyline::{
|
||||
completion::Completer, highlight::Highlighter, hint::Hinter, validate::Validator, Context,
|
||||
Helper,
|
||||
};
|
||||
impl Completer for ShellHelper<'_> {
|
||||
type Candidate = String;
|
||||
|
||||
fn complete(
|
||||
&self,
|
||||
line: &str,
|
||||
pos: usize,
|
||||
_ctx: &Context,
|
||||
) -> rustyline::Result<(usize, Vec<String>)> {
|
||||
Ok(self
|
||||
.complete_opt(&line[0..pos])
|
||||
// as far as I can tell, there's no better way to do both completion
|
||||
// and indentation (or even just indentation)
|
||||
.unwrap_or_else(|| (line.len(), vec!["\t".to_owned()])))
|
||||
fn complete(
|
||||
&self,
|
||||
line: &str,
|
||||
pos: usize,
|
||||
_ctx: &Context,
|
||||
) -> rustyline::Result<(usize, Vec<String>)> {
|
||||
Ok(self
|
||||
.complete_opt(&line[0..pos])
|
||||
// as far as I can tell, there's no better way to do both completion
|
||||
// and indentation (or even just indentation)
|
||||
.unwrap_or_else(|| (line.len(), vec!["\t".to_owned()])))
|
||||
}
|
||||
}
|
||||
|
||||
impl Hinter for ShellHelper<'_> {}
|
||||
impl Highlighter for ShellHelper<'_> {}
|
||||
impl Validator for ShellHelper<'_> {}
|
||||
impl Helper for ShellHelper<'_> {}
|
||||
}
|
||||
}
|
||||
|
||||
impl Hinter for ShellHelper<'_> {}
|
||||
impl Highlighter for ShellHelper<'_> {}
|
||||
impl Validator for ShellHelper<'_> {}
|
||||
impl Helper for ShellHelper<'_> {}
|
||||
@@ -89,6 +89,7 @@ gethostname = "0.2.0"
|
||||
subprocess = "0.2.2"
|
||||
num_cpus = "1"
|
||||
socket2 = { version = "0.3", features = ["unix"] }
|
||||
rustyline = "6.0"
|
||||
|
||||
[target.'cfg(not(any(target_arch = "wasm32", target_os = "redox")))'.dependencies]
|
||||
dns-lookup = "1.0"
|
||||
|
||||
+20
-1
@@ -30,6 +30,7 @@ use crate::pyobject::{
|
||||
Either, IdProtocol, ItemProtocol, PyIterable, PyObjectRef, PyResult, PyValue, TryFromObject,
|
||||
TypeProtocol,
|
||||
};
|
||||
use crate::readline::{Readline, ReadlineResult};
|
||||
use crate::scope::Scope;
|
||||
use crate::stdlib::ast;
|
||||
use crate::vm::VirtualMachine;
|
||||
@@ -345,7 +346,22 @@ fn builtin_id(obj: PyObjectRef) -> usize {
|
||||
obj.get_id()
|
||||
}
|
||||
|
||||
// builtin_input
|
||||
fn builtin_input(prompt: OptionalArg<PyStringRef>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
let prompt = prompt.as_ref().map_or("", |s| s.as_str());
|
||||
let mut readline = Readline::new(());
|
||||
match readline.readline(prompt) {
|
||||
ReadlineResult::Line(s) => Ok(s),
|
||||
ReadlineResult::EOF => Err(vm.new_exception_empty(vm.ctx.exceptions.eof_error.clone())),
|
||||
ReadlineResult::Interrupt => {
|
||||
Err(vm.new_exception_empty(vm.ctx.exceptions.keyboard_interrupt.clone()))
|
||||
}
|
||||
ReadlineResult::IO(e) => Err(vm.new_os_error(e.to_string())),
|
||||
ReadlineResult::EncodingError => {
|
||||
Err(vm.new_unicode_decode_error("Error decoding readline input".to_owned()))
|
||||
}
|
||||
ReadlineResult::Other(e) => Err(vm.new_runtime_error(e.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
fn builtin_isinstance(obj: PyObjectRef, typ: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
single_or_tuple_any(
|
||||
@@ -797,6 +813,7 @@ pub fn make_module(vm: &VirtualMachine, module: PyObjectRef) {
|
||||
"hash" => ctx.new_function(builtin_hash),
|
||||
"hex" => ctx.new_function(builtin_hex),
|
||||
"id" => ctx.new_function(builtin_id),
|
||||
"input" => ctx.new_function(builtin_input),
|
||||
"int" => ctx.int_type(),
|
||||
"isinstance" => ctx.new_function(builtin_isinstance),
|
||||
"issubclass" => ctx.new_function(builtin_issubclass),
|
||||
@@ -866,6 +883,8 @@ pub fn make_module(vm: &VirtualMachine, module: PyObjectRef) {
|
||||
"NameError" => ctx.exceptions.name_error.clone(),
|
||||
"UnboundLocalError" => ctx.exceptions.unbound_local_error.clone(),
|
||||
"OSError" => ctx.exceptions.os_error.clone(),
|
||||
// OSError alias
|
||||
"IOError" => ctx.exceptions.os_error.clone(),
|
||||
"BlockingIOError" => ctx.exceptions.blocking_io_error.clone(),
|
||||
"ChildProcessError" => ctx.exceptions.child_process_error.clone(),
|
||||
"ConnectionError" => ctx.exceptions.connection_error.clone(),
|
||||
|
||||
+1
-4
@@ -987,10 +987,7 @@ impl Frame {
|
||||
0 => match vm.current_exception() {
|
||||
Some(exc) => exc,
|
||||
None => {
|
||||
return Err(vm.new_exception_msg(
|
||||
vm.ctx.exceptions.runtime_error.clone(),
|
||||
"No active exception to reraise".to_owned(),
|
||||
))
|
||||
return Err(vm.new_runtime_error("No active exception to reraise".to_owned()))
|
||||
}
|
||||
},
|
||||
1 | 2 => ExceptionCtor::try_from_object(vm, self.pop_value())?.instantiate(vm)?,
|
||||
|
||||
@@ -70,6 +70,7 @@ pub mod obj;
|
||||
pub mod py_serde;
|
||||
mod pyhash;
|
||||
pub mod pyobject;
|
||||
pub mod readline;
|
||||
pub mod scope;
|
||||
mod sequence;
|
||||
pub mod slots;
|
||||
|
||||
@@ -89,10 +89,9 @@ impl PyCoroutine {
|
||||
vm.frames.borrow_mut().pop();
|
||||
self.closed.set(true);
|
||||
match result {
|
||||
Ok(ExecutionResult::Yield(_)) => Err(vm.new_exception_msg(
|
||||
vm.ctx.exceptions.runtime_error.clone(),
|
||||
"generator ignored GeneratorExit".to_owned(),
|
||||
)),
|
||||
Ok(ExecutionResult::Yield(_)) => {
|
||||
Err(vm.new_runtime_error("generator ignored GeneratorExit".to_owned()))
|
||||
}
|
||||
Err(e) => {
|
||||
if isinstance(&e, &vm.ctx.exceptions.generator_exit) {
|
||||
Ok(())
|
||||
|
||||
@@ -564,10 +564,9 @@ macro_rules! dict_iterator {
|
||||
let mut position = self.position.get();
|
||||
let dict = self.dict.entries.borrow();
|
||||
if dict.has_changed_size(&self.size) {
|
||||
return Err(vm.new_exception_msg(
|
||||
vm.ctx.exceptions.runtime_error.clone(),
|
||||
"dictionary changed size during iteration".to_owned(),
|
||||
));
|
||||
return Err(
|
||||
vm.new_runtime_error("dictionary changed size during iteration".to_owned())
|
||||
);
|
||||
}
|
||||
match dict.next_entry(&mut position) {
|
||||
Some((key, value)) => {
|
||||
|
||||
@@ -102,10 +102,9 @@ impl PyGenerator {
|
||||
vm.frames.borrow_mut().pop();
|
||||
self.closed.set(true);
|
||||
match result {
|
||||
Ok(ExecutionResult::Yield(_)) => Err(vm.new_exception_msg(
|
||||
vm.ctx.exceptions.runtime_error.clone(),
|
||||
"generator ignored GeneratorExit".to_owned(),
|
||||
)),
|
||||
Ok(ExecutionResult::Yield(_)) => {
|
||||
Err(vm.new_runtime_error("generator ignored GeneratorExit".to_owned()))
|
||||
}
|
||||
Err(e) => {
|
||||
if isinstance(&e, &vm.ctx.exceptions.generator_exit) {
|
||||
Ok(())
|
||||
|
||||
+4
-4
@@ -682,10 +682,10 @@ impl<T: PyValue> PyRef<T> {
|
||||
if obj.payload_is::<T>() {
|
||||
Ok(Self::new_ref_unchecked(obj))
|
||||
} else {
|
||||
Err(vm.new_exception_msg(
|
||||
vm.ctx.exceptions.runtime_error.clone(),
|
||||
format!("Unexpected payload for type {:?}", obj.class().name),
|
||||
))
|
||||
Err(vm.new_runtime_error(format!(
|
||||
"Unexpected payload for type {:?}",
|
||||
obj.class().name
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
use rustpython_vm::{scope::Scope, VirtualMachine};
|
||||
|
||||
type OtherError = Box<dyn std::error::Error>;
|
||||
type OtherResult<T> = Result<T, OtherError>;
|
||||
|
||||
@@ -19,13 +17,16 @@ pub enum ReadlineResult {
|
||||
mod basic_readline {
|
||||
use super::*;
|
||||
|
||||
pub struct BasicReadline<'vm> {
|
||||
vm: &'vm VirtualMachine,
|
||||
pub trait Helper {}
|
||||
impl<T> Helper for T {}
|
||||
|
||||
pub struct Readline<H: Helper> {
|
||||
helper: H,
|
||||
}
|
||||
|
||||
impl<'vm> BasicReadline<'vm> {
|
||||
pub fn new(vm: &'vm VirtualMachine, _scope: Scope) -> Self {
|
||||
BasicReadline { vm }
|
||||
impl<H: Helper> Readline<H> {
|
||||
pub fn new(helper: H) -> Self {
|
||||
Readline { helper }
|
||||
}
|
||||
|
||||
pub fn load_history(&mut self, _path: &Path) -> OtherResult<()> {
|
||||
@@ -60,16 +61,19 @@ mod basic_readline {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "wasi"))]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
mod rustyline_readline {
|
||||
use super::{super::rustyline_helper::ShellHelper, *};
|
||||
use super::*;
|
||||
|
||||
pub struct RustylineReadline<'vm> {
|
||||
repl: rustyline::Editor<ShellHelper<'vm>>,
|
||||
pub trait Helper: rustyline::Helper {}
|
||||
impl<T: rustyline::Helper> Helper for T {}
|
||||
|
||||
pub struct Readline<H: Helper> {
|
||||
repl: rustyline::Editor<H>,
|
||||
}
|
||||
|
||||
impl<'vm> RustylineReadline<'vm> {
|
||||
pub fn new(vm: &'vm VirtualMachine, scope: Scope) -> Self {
|
||||
impl<H: Helper> Readline<H> {
|
||||
pub fn new(helper: H) -> Self {
|
||||
use rustyline::{At, Cmd, CompletionType, Config, Editor, KeyPress, Movement, Word};
|
||||
let mut repl = Editor::with_config(
|
||||
Config::builder()
|
||||
@@ -85,8 +89,8 @@ mod rustyline_readline {
|
||||
KeyPress::ControlRight,
|
||||
Cmd::Move(Movement::ForwardWord(1, At::AfterEnd, Word::Vi)),
|
||||
);
|
||||
repl.set_helper(Some(ShellHelper::new(vm, scope)));
|
||||
RustylineReadline { repl }
|
||||
repl.set_helper(Some(helper));
|
||||
Readline { repl }
|
||||
}
|
||||
|
||||
pub fn load_history(&mut self, path: &Path) -> OtherResult<()> {
|
||||
@@ -126,17 +130,18 @@ mod rustyline_readline {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "wasi")]
|
||||
type ReadlineInner<'vm> = basic_readline::BasicReadline<'vm>;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use basic_readline as readline_inner;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use rustyline_readline as readline_inner;
|
||||
|
||||
#[cfg(not(target_os = "wasi"))]
|
||||
type ReadlineInner<'vm> = rustyline_readline::RustylineReadline<'vm>;
|
||||
pub use readline_inner::Helper;
|
||||
|
||||
pub struct Readline<'vm>(ReadlineInner<'vm>);
|
||||
pub struct Readline<H: Helper>(readline_inner::Readline<H>);
|
||||
|
||||
impl<'vm> Readline<'vm> {
|
||||
pub fn new(vm: &'vm VirtualMachine, scope: Scope) -> Self {
|
||||
Readline(ReadlineInner::new(vm, scope))
|
||||
impl<H: Helper> Readline<H> {
|
||||
pub fn new(helper: H) -> Self {
|
||||
Readline(readline_inner::Readline::new(helper))
|
||||
}
|
||||
pub fn load_history(&mut self, path: &Path) -> OtherResult<()> {
|
||||
self.0.load_history(path)
|
||||
+1
-4
@@ -205,10 +205,7 @@ pub fn convert_nix_error(vm: &VirtualMachine, err: nix::Error) -> PyBaseExceptio
|
||||
let exc_type = vm.ctx.exceptions.unicode_error.clone();
|
||||
vm.new_exception_msg(exc_type, err.to_string())
|
||||
}
|
||||
nix::Error::UnsupportedOperation => {
|
||||
let exc_type = vm.ctx.exceptions.runtime_error.clone();
|
||||
vm.new_exception_msg(exc_type, err.to_string())
|
||||
}
|
||||
nix::Error::UnsupportedOperation => vm.new_runtime_error(err.to_string()),
|
||||
nix::Error::Sys(errno) => {
|
||||
let exc_type = convert_nix_errno(vm, errno);
|
||||
vm.new_exception_msg(exc_type, err.to_string())
|
||||
|
||||
@@ -520,6 +520,11 @@ impl VirtualMachine {
|
||||
self.new_exception_msg(import_error, msg)
|
||||
}
|
||||
|
||||
pub fn new_runtime_error(&self, msg: String) -> PyBaseExceptionRef {
|
||||
let runtime_error = self.ctx.exceptions.runtime_error.clone();
|
||||
self.new_exception_msg(runtime_error, msg)
|
||||
}
|
||||
|
||||
// TODO: #[track_caller] when stabilized
|
||||
fn _py_panic_failed(&self, exc: &PyBaseExceptionRef, msg: &str) -> ! {
|
||||
let show_backtrace = env::var_os("RUST_BACKTRACE").map_or(false, |v| &v != "0");
|
||||
|
||||
Reference in new issue
Block a user