Skip to content

Commit 0129bb5

Browse files
Merge pull request RustPython#1226 from palaviv/subprocess-2
Extend subprocess.Popen
2 parents 28eb496 + f790de2 commit 0129bb5

File tree

5 files changed

+99
-6
lines changed

5 files changed

+99
-6
lines changed

tests/snippets/stdlib_subprocess.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import subprocess
22
import time
33
import sys
4+
import signal
45

56
from testutils import assertRaises
67

@@ -29,7 +30,29 @@
2930

3031
if "win" not in sys.platform:
3132
# unix
32-
assert p.stdout.read() == b"test\n"
33+
test_output = b"test\n"
3334
else:
3435
# windows
35-
assert p.stdout.read() == b"test\r\n"
36+
test_output = b"test\r\n"
37+
38+
assert p.stdout.read() == test_output
39+
40+
p = subprocess.Popen(["sleep", "2"])
41+
p.terminate()
42+
p.wait()
43+
if "win" not in sys.platform:
44+
assert p.returncode == -signal.SIGTERM
45+
else:
46+
assert p.returncode == 1
47+
48+
p = subprocess.Popen(["sleep", "2"])
49+
p.kill()
50+
p.wait()
51+
if "win" not in sys.platform:
52+
assert p.returncode == -signal.SIGKILL
53+
else:
54+
assert p.returncode == 1
55+
56+
p = subprocess.Popen(["echo", "test"], stdout=subprocess.PIPE)
57+
(stdout, stderr) = p.communicate()
58+
assert stdout == test_output

vm/src/obj/objbytes.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ use std::ops::Deref;
1111

1212
use crate::function::OptionalArg;
1313
use crate::pyobject::{
14-
PyClassImpl, PyContext, PyIterable, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject,
14+
IntoPyObject, PyClassImpl, PyContext, PyIterable, PyObjectRef, PyRef, PyResult, PyValue,
15+
TryFromObject,
1516
};
1617

1718
use super::objbyteinner::{
@@ -59,6 +60,12 @@ impl PyBytes {
5960
}
6061
}
6162

63+
impl IntoPyObject for Vec<u8> {
64+
fn into_pyobject(self, vm: &VirtualMachine) -> PyResult {
65+
Ok(vm.ctx.new_bytes(self))
66+
}
67+
}
68+
6269
impl Deref for PyBytes {
6370
type Target = [u8];
6471

vm/src/obj/objtuple.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ use std::fmt;
33

44
use crate::function::OptionalArg;
55
use crate::pyhash;
6-
use crate::pyobject::{IdProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue};
6+
use crate::pyobject::{
7+
IdProtocol, IntoPyObject, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue,
8+
};
79
use crate::vm::{ReprGuard, VirtualMachine};
810

911
use super::objbool;
@@ -37,6 +39,27 @@ impl PyValue for PyTuple {
3739
}
3840
}
3941

42+
impl<A> IntoPyObject for (A,)
43+
where
44+
A: IntoPyObject,
45+
{
46+
fn into_pyobject(self, vm: &VirtualMachine) -> PyResult {
47+
Ok(vm.ctx.new_tuple(vec![self.0.into_pyobject(vm)?]))
48+
}
49+
}
50+
51+
impl<A, B> IntoPyObject for (A, B)
52+
where
53+
A: IntoPyObject,
54+
B: IntoPyObject,
55+
{
56+
fn into_pyobject(self, vm: &VirtualMachine) -> PyResult {
57+
Ok(vm
58+
.ctx
59+
.new_tuple(vec![self.0.into_pyobject(vm)?, self.1.into_pyobject(vm)?]))
60+
}
61+
}
62+
4063
impl PyTuple {
4164
pub fn fast_getitem(&self, idx: usize) -> PyObjectRef {
4265
self.elements[idx].clone()

vm/src/stdlib/os.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ pub fn os_open(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
159159
Ok(vm.ctx.new_int(raw_file_number(handle)))
160160
}
161161

162-
fn convert_io_error(vm: &VirtualMachine, err: io::Error) -> PyObjectRef {
162+
pub fn convert_io_error(vm: &VirtualMachine, err: io::Error) -> PyObjectRef {
163163
let os_error = match err.kind() {
164164
ErrorKind::NotFound => {
165165
let exc_type = vm.ctx.exceptions.file_not_found_error.clone();

vm/src/stdlib/subprocess.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
use std::cell::RefCell;
2+
use std::ffi::OsString;
23
use std::fs::File;
34
use std::time::Duration;
45

56
use subprocess;
67

78
use crate::function::OptionalArg;
9+
use crate::obj::objbytes::PyBytesRef;
810
use crate::obj::objlist::PyListRef;
911
use crate::obj::objsequence;
1012
use crate::obj::objstr::{self, PyStringRef};
1113
use crate::obj::objtype::PyClassRef;
1214
use crate::pyobject::{Either, IntoPyObject, PyObjectRef, PyRef, PyResult, PyValue};
1315
use crate::stdlib::io::io_open;
14-
use crate::stdlib::os::{raw_file_number, rust_file};
16+
use crate::stdlib::os::{convert_io_error, raw_file_number, rust_file};
1517
use crate::vm::VirtualMachine;
1618

1719
#[derive(Debug)]
@@ -37,6 +39,8 @@ struct PopenArgs {
3739
stdout: Option<i64>,
3840
#[pyarg(positional_or_keyword, default = "None")]
3941
stderr: Option<i64>,
42+
#[pyarg(positional_or_keyword, default = "None")]
43+
cwd: Option<PyStringRef>,
4044
}
4145

4246
impl IntoPyObject for subprocess::ExitStatus {
@@ -95,13 +99,15 @@ impl PopenRef {
9599
.map(|x| objstr::get_value(x))
96100
.collect(),
97101
};
102+
let cwd = args.cwd.map(|x| OsString::from(x.as_str()));
98103

99104
let process = subprocess::Popen::create(
100105
&command_list,
101106
subprocess::PopenConfig {
102107
stdin,
103108
stdout,
104109
stderr,
110+
cwd,
105111
..Default::default()
106112
},
107113
)
@@ -149,6 +155,36 @@ impl PopenRef {
149155
fn stderr(self, vm: &VirtualMachine) -> PyResult {
150156
convert_to_file_io(&self.process.borrow().stderr, "rb".to_string(), vm)
151157
}
158+
159+
fn terminate(self, vm: &VirtualMachine) -> PyResult<()> {
160+
self.process
161+
.borrow_mut()
162+
.terminate()
163+
.map_err(|err| convert_io_error(vm, err))
164+
}
165+
166+
fn kill(self, vm: &VirtualMachine) -> PyResult<()> {
167+
self.process
168+
.borrow_mut()
169+
.kill()
170+
.map_err(|err| convert_io_error(vm, err))
171+
}
172+
173+
#[allow(clippy::type_complexity)]
174+
fn communicate(
175+
self,
176+
stdin: OptionalArg<PyBytesRef>,
177+
vm: &VirtualMachine,
178+
) -> PyResult<(Option<Vec<u8>>, Option<Vec<u8>>)> {
179+
self.process
180+
.borrow_mut()
181+
.communicate_bytes(stdin.into_option().as_ref().map(|bytes| bytes.get_value()))
182+
.map_err(|err| convert_io_error(vm, err))
183+
}
184+
185+
fn pid(self, _vm: &VirtualMachine) -> Option<u32> {
186+
self.process.borrow().pid()
187+
}
152188
}
153189

154190
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
@@ -165,6 +201,10 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
165201
"stdin" => ctx.new_property(PopenRef::stdin),
166202
"stdout" => ctx.new_property(PopenRef::stdout),
167203
"stderr" => ctx.new_property(PopenRef::stderr),
204+
"terminate" => ctx.new_rustfunc(PopenRef::terminate),
205+
"kill" => ctx.new_rustfunc(PopenRef::kill),
206+
"communicate" => ctx.new_rustfunc(PopenRef::communicate),
207+
"pid" => ctx.new_property(PopenRef::pid),
168208
});
169209

170210
let module = py_module!(vm, "subprocess", {

0 commit comments

Comments
 (0)