Support redirection

This commit is contained in:
Aviv Palivoda
2019-08-03 21:08:10 +03:00
parent 72143a6a5d
commit b08c70b4e7
3 changed files with 101 additions and 5 deletions

View File

@@ -22,3 +22,8 @@ with assertRaises(subprocess.TimeoutExpired):
p.wait()
assert p.returncode == 0
p = subprocess.Popen(["echo", "-n", "test"], stdout=subprocess.PIPE)
p.wait()
assert p.stdout.read() == b"test"

View File

@@ -587,7 +587,7 @@ pub fn io_open(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [(file, Some(vm.ctx.str_type()))],
required = [(file, None)],
optional = [(mode, Some(vm.ctx.str_type()))]
);

View File

@@ -3,12 +3,14 @@ use std::time::Duration;
use subprocess;
use crate::function::OptionalArg;
use crate::function::{OptionalArg, PyFuncArgs};
use crate::obj::objlist::PyListRef;
use crate::obj::objsequence;
use crate::obj::objstr::{self, PyStringRef};
use crate::obj::objtype::PyClassRef;
use crate::pyobject::{Either, IntoPyObject, PyObjectRef, PyRef, PyResult, PyValue};
use crate::stdlib::io::io_open;
use crate::stdlib::os::{raw_file_number, rust_file};
use crate::vm::VirtualMachine;
#[derive(Debug)]
@@ -28,6 +30,12 @@ type PopenRef = PyRef<Popen>;
struct PopenArgs {
#[pyarg(positional_only)]
args: Either<PyStringRef, PyListRef>,
#[pyarg(positional_or_keyword, default = "None")]
stdin: Option<i64>,
#[pyarg(positional_or_keyword, default = "None")]
stdout: Option<i64>,
#[pyarg(positional_or_keyword, default = "None")]
stderr: Option<i64>,
}
impl IntoPyObject for subprocess::ExitStatus {
@@ -42,8 +50,29 @@ impl IntoPyObject for subprocess::ExitStatus {
}
}
fn convert_redirection(arg: Option<i64>, vm: &VirtualMachine) -> PyResult<subprocess::Redirection> {
match arg {
Some(fd) => match fd {
-1 => Ok(subprocess::Redirection::Pipe),
-2 => panic!("TODO"),
-3 => panic!("TODO"),
fd => {
if fd < 0 {
Err(vm.new_value_error(format!("Invalid fd: {}", fd)))
} else {
Ok(subprocess::Redirection::File(rust_file(fd)))
}
}
},
None => Ok(subprocess::Redirection::None),
}
}
impl PopenRef {
fn new(cls: PyClassRef, args: PopenArgs, vm: &VirtualMachine) -> PyResult<PopenRef> {
let stdin = convert_redirection(args.stdin, vm)?;
let stdout = convert_redirection(args.stdout, vm)?;
let stderr = convert_redirection(args.stderr, vm)?;
let command_list = match args.args {
Either::A(command) => vec![command.as_str().to_string()],
Either::B(command_list) => objsequence::get_elements_list(command_list.as_object())
@@ -52,8 +81,16 @@ impl PopenRef {
.collect(),
};
let process = subprocess::Popen::create(&command_list, subprocess::PopenConfig::default())
.map_err(|s| vm.new_os_error(format!("Could not start program: {}", s)))?;
let process = subprocess::Popen::create(
&command_list,
subprocess::PopenConfig {
stdin,
stdout,
stderr,
..Default::default()
},
)
.map_err(|s| vm.new_os_error(format!("Could not start program: {}", s)))?;
Popen {
process: RefCell::new(process),
@@ -85,6 +122,54 @@ impl PopenRef {
Ok(())
}
}
fn stdin(self, vm: &VirtualMachine) -> PyResult {
match self.process.borrow().stdin {
Some(ref stdin) => io_open(
vm,
PyFuncArgs::new(
vec![
vm.new_int(raw_file_number(stdin.try_clone().unwrap())),
vm.new_str("wb".to_string()),
],
vec![],
),
),
None => Ok(vm.get_none()),
}
}
fn stdout(self, vm: &VirtualMachine) -> PyResult {
match self.process.borrow().stdout {
Some(ref stdout) => io_open(
vm,
PyFuncArgs::new(
vec![
vm.new_int(raw_file_number(stdout.try_clone().unwrap())),
vm.new_str("rb".to_string()),
],
vec![],
),
),
None => Ok(vm.get_none()),
}
}
fn stderr(self, vm: &VirtualMachine) -> PyResult {
match self.process.borrow().stderr {
Some(ref stderr) => io_open(
vm,
PyFuncArgs::new(
vec![
vm.new_int(raw_file_number(stderr.try_clone().unwrap())),
vm.new_str("rb".to_string()),
],
vec![],
),
),
None => Ok(vm.get_none()),
}
}
}
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
@@ -97,13 +182,19 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
"__new__" => ctx.new_rustfunc(PopenRef::new),
"poll" => ctx.new_rustfunc(PopenRef::poll),
"returncode" => ctx.new_property(PopenRef::return_code),
"wait" => ctx.new_rustfunc(PopenRef::wait)
"wait" => ctx.new_rustfunc(PopenRef::wait),
"stdin" => ctx.new_property(PopenRef::stdin),
"stdout" => ctx.new_property(PopenRef::stdout),
"stderr" => ctx.new_property(PopenRef::stderr),
});
let module = py_module!(vm, "subprocess", {
"Popen" => popen,
"SubprocessError" => subprocess_error,
"TimeoutExpired" => timeout_expired,
"PIPE" => ctx.new_int(-1),
"STDOUT" => ctx.new_int(-2),
"DEVNULL" => ctx.new_int(-3),
});
module