Merge pull request #1962 from RustPython/coolreader18/subprocess-py

Implement subprocess using _posixsubprocess and _winapi
This commit is contained in:
Noah authored and GitHub committed 2020-06-26 10:07:29 -05:00
commit 31c9947b90
19 files changed
+2290 -470

No files matched your search

Generated
+1 -11
View File
@@ -1633,6 +1633,7 @@ dependencies = [
"bstr",
"byteorder 1.3.4",
"caseless",
"cfg-if",
"chrono",
"crc",
"crc32fast",
@@ -1694,7 +1695,6 @@ dependencies = [
"smallbox",
"socket2",
"statrs",
"subprocess",
"thread_local",
"uname",
"unic-bidi",
@@ -1962,16 +1962,6 @@ version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
[[package]]
name = "subprocess"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5109e3c3215ce5339ca9766a6db7f39144ccc7d9bad53b96d65cfb14ae6c2985"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "subtle"
version = "1.0.0"
+1 -2
View File
@@ -9,8 +9,7 @@ if sys.platform != 'win32': # pragma: no cover
import _winapi
import itertools
# XXX RustPython TODO: msvcrt
# import msvcrt
import msvcrt
import os
import socket
import subprocess
+3 -4
View File
@@ -281,10 +281,9 @@ def _syscmd_ver(system='', release='', version='',
for cmd in ('ver', 'command /c ver', 'cmd /c ver'):
try:
info = subprocess.check_output(cmd,
stderr=subprocess.DEVNULL,)
# XXX RustPython TODO: more Popen args
# text=True,
# shell=True)
stderr=subprocess.DEVNULL,
text=True,
shell=True)
except (OSError, subprocess.CalledProcessError) as why:
#print('Command %s failed: %s' % (cmd, why))
continue
+1540 -99
View File
File diff suppressed because it is too large. Load diff
+2 -1
View File
@@ -2442,7 +2442,8 @@ def strip_python_stderr(stderr):
This will typically be run on the result of the communicate() method
of a subprocess.Popen object.
"""
stderr = re.sub(br"\[\d+ refs, \d+ blocks\]\r?\n?", b"", stderr).strip()
# XXX RustPython TODO: bytes regexes
# stderr = re.sub(br"\[\d+ refs, \d+ blocks\]\r?\n?", b"", stderr).strip()
return stderr
requires_type_collecting = unittest.skipIf(hasattr(sys, 'getcounts'),
+1 -1
View File
@@ -7,7 +7,7 @@ from test import support
from test.support.script_helper import assert_python_ok
@unittest.skip("TODO: RUSTPYTHON") # Need to fix subprocess to take env
@unittest.skip("TODO: RUSTPYTHON, rustpython -X faulthandler")
class TestTool(unittest.TestCase):
data = """
+20 -8
View File
@@ -5,7 +5,20 @@ import signal
from testutils import assert_raises
p = subprocess.Popen(["echo", "test"])
is_unix = not sys.platform.startswith("win")
if is_unix:
def echo(text):
return ["echo", text]
def sleep(secs):
return ["sleep", str(secs)]
else:
def echo(text):
return ["cmd", "/C", f"echo {text}"]
def sleep(secs):
# TODO: make work in a non-unixy environment (something with timeout.exe?)
return ["sleep", str(secs)]
p = subprocess.Popen(echo("test"))
time.sleep(0.1)
@@ -14,7 +27,7 @@ assert p.returncode is None
assert p.poll() == 0
assert p.returncode == 0
p = subprocess.Popen(["sleep", "2"])
p = subprocess.Popen(sleep(2))
assert p.poll() is None
@@ -25,14 +38,13 @@ p.wait()
assert p.returncode == 0
p = subprocess.Popen(["echo", "test"], stdout=subprocess.PIPE)
p = subprocess.Popen(echo("test"), stdout=subprocess.PIPE)
p.wait()
is_unix = "win" not in sys.platform or "darwin" in sys.platform
assert p.stdout.read().strip() == b"test"
p = subprocess.Popen(["sleep", "2"])
p = subprocess.Popen(sleep(2))
p.terminate()
p.wait()
if is_unix:
@@ -40,7 +52,7 @@ if is_unix:
else:
assert p.returncode == 1
p = subprocess.Popen(["sleep", "2"])
p = subprocess.Popen(sleep(2))
p.kill()
p.wait()
if is_unix:
@@ -48,10 +60,10 @@ if is_unix:
else:
assert p.returncode == 1
p = subprocess.Popen(["echo", "test"], stdout=subprocess.PIPE)
p = subprocess.Popen(echo("test"), stdout=subprocess.PIPE)
(stdout, stderr) = p.communicate()
assert stdout.strip() == b"test"
p = subprocess.Popen(["sleep", "5"], stdout=subprocess.PIPE)
p = subprocess.Popen(sleep(5), stdout=subprocess.PIPE)
with assert_raises(subprocess.TimeoutExpired):
p.communicate(timeout=1)
+8 -2
View File
@@ -73,6 +73,7 @@ crossbeam-utils = "0.7"
generational-arena = "0.2"
parking_lot = "0.11"
thread_local = "1.0"
cfg-if = "0.1.10"
## unicode stuff
unicode_names2 = "0.4"
@@ -101,7 +102,6 @@ uname = "0.1.1"
crc32fast = "1.2.0"
adler32 = "1.0.3"
gethostname = "0.2.0"
subprocess = "0.2.2"
socket2 = "0.3"
rustyline = "6.0"
openssl = { version = "0.10", features = ["vendored"], optional = true }
@@ -122,7 +122,13 @@ schannel = "0.1"
[target.'cfg(windows)'.dependencies.winapi]
version = "0.3"
features = ["winsock2", "handleapi", "ws2def", "std", "winbase", "wincrypt", "fileapi", "wincon"]
features = [
"winsock2", "handleapi", "ws2def", "std", "winbase", "wincrypt", "fileapi", "processenv",
"namedpipeapi", "winnt", "processthreadsapi", "errhandlingapi", "winuser", "synchapi",
]
[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen = "0.2"
[build-dependencies]
itertools = "0.8"
+19
View File
@@ -1,3 +1,7 @@
use itertools::Itertools;
use std::env;
use std::io::prelude::*;
use std::path::PathBuf;
use std::process::Command;
fn main() {
@@ -8,6 +12,21 @@ fn main() {
);
println!("cargo:rustc-env=RUSTPYTHON_GIT_TAG={}", git_tag());
println!("cargo:rustc-env=RUSTPYTHON_GIT_BRANCH={}", git_branch());
println!(
"cargo:rustc-env=RUSTPYTHON_TARGET_TRIPLE={}",
env::var("TARGET").unwrap()
);
let mut env_path = PathBuf::from(env::var_os("OUT_DIR").unwrap());
env_path.push("env_vars.rs");
let mut f = std::fs::File::create(env_path).unwrap();
write!(
f,
"hashmap! {{ {} }}",
std::env::vars_os().format_with(", ", |(k, v), f| f(&format_args!("{:?} => {:?}", k, v)))
)
.unwrap();
}
fn git_hash() -> String {
+3
View File
@@ -183,6 +183,9 @@ impl PyDictRef {
pub fn len(self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.len() == 0
}
#[pymethod(magic)]
fn sizeof(self) -> usize {
+17
View File
@@ -1383,6 +1383,23 @@ where
pub type PyComparisonValue = PyArithmaticValue<bool>;
#[derive(Clone)]
pub struct PySequence<T = PyObjectRef>(Vec<T>);
impl<T> PySequence<T> {
pub fn into_vec(self) -> Vec<T> {
self.0
}
pub fn as_slice(&self) -> &[T] {
&self.0
}
}
impl<T: TryFromObject> TryFromObject for PySequence<T> {
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
vm.extract_elements(&obj).map(Self)
}
}
#[cfg(test)]
mod tests {
use super::*;
+12 -3
View File
@@ -30,6 +30,7 @@ pub mod socket;
mod string;
#[cfg(feature = "rustpython-compiler")]
mod symtable;
mod sysconfigdata;
#[cfg(not(target_arch = "wasm32"))]
mod thread;
mod time_module;
@@ -49,6 +50,8 @@ mod faulthandler;
mod msvcrt;
#[cfg(not(target_arch = "wasm32"))]
mod multiprocessing;
#[cfg(unix)]
mod posixsubprocess;
#[cfg(all(unix, not(any(target_os = "android", target_os = "redox"))))]
mod pwd;
#[cfg(not(target_arch = "wasm32"))]
@@ -57,8 +60,6 @@ mod select;
pub mod signal;
#[cfg(all(not(target_arch = "wasm32"), feature = "ssl"))]
mod ssl;
#[cfg(not(target_arch = "wasm32"))]
mod subprocess;
#[cfg(windows)]
mod winapi;
#[cfg(windows)]
@@ -95,6 +96,7 @@ pub fn get_module_inits() -> HashMap<String, StdlibInitFunc> {
"_imp".to_owned() => Box::new(imp::make_module),
"unicodedata".to_owned() => Box::new(unicodedata::make_module),
"_warnings".to_owned() => Box::new(warnings::make_module),
crate::sysmodule::sysconfigdata_name() => Box::new(sysconfigdata::make_module),
};
// Insert parser related modules:
@@ -129,7 +131,6 @@ pub fn get_module_inits() -> HashMap<String, StdlibInitFunc> {
modules.insert("select".to_owned(), Box::new(select::make_module));
#[cfg(feature = "ssl")]
modules.insert("_ssl".to_owned(), Box::new(ssl::make_module));
modules.insert("_subprocess".to_owned(), Box::new(subprocess::make_module));
modules.insert("_thread".to_owned(), Box::new(thread::make_module));
#[cfg(not(target_os = "redox"))]
modules.insert("zlib".to_owned(), Box::new(zlib::make_module));
@@ -145,6 +146,14 @@ pub fn get_module_inits() -> HashMap<String, StdlibInitFunc> {
modules.insert("pwd".to_owned(), Box::new(pwd::make_module));
}
#[cfg(unix)]
{
modules.insert(
"_posixsubprocess".to_owned(),
Box::new(posixsubprocess::make_module),
);
}
// Windows-only
#[cfg(windows)]
{
+15 -1
View File
@@ -59,9 +59,22 @@ fn msvcrt_setmode(fd: i32, flags: i32, vm: &VirtualMachine) -> PyResult<i32> {
}
}
extern "C" {
fn _open_osfhandle(osfhandle: isize, flags: i32) -> i32;
}
fn msvcrt_open_osfhandle(handle: isize, flags: i32, vm: &VirtualMachine) -> PyResult<i32> {
let ret = unsafe { suppress_iph!(_open_osfhandle(handle, flags)) };
if ret == -1 {
Err(errno_err(vm))
} else {
Ok(ret)
}
}
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
let ctx = &vm.ctx;
py_module!(vm, "_msvcrt", {
py_module!(vm, "msvcrt", {
"getch" => ctx.new_function(msvcrt_getch),
"getwch" => ctx.new_function(msvcrt_getwch),
"getche" => ctx.new_function(msvcrt_getche),
@@ -69,5 +82,6 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
"putch" => ctx.new_function(msvcrt_putch),
"putwch" => ctx.new_function(msvcrt_putwch),
"setmode" => ctx.new_function(msvcrt_setmode),
"open_osfhandle" => ctx.new_function(msvcrt_open_osfhandle),
})
}
+26 -18
View File
@@ -28,7 +28,7 @@ use crate::exceptions::PyBaseExceptionRef;
use crate::function::{IntoPyNativeFunc, OptionalArg, PyFuncArgs};
use crate::obj::objbyteinner::PyBytesLike;
use crate::obj::objbytes::{PyBytes, PyBytesRef};
use crate::obj::objdict::{PyDictRef, PyMapping};
use crate::obj::objdict::PyDictRef;
use crate::obj::objint::PyIntRef;
use crate::obj::objiter;
use crate::obj::objset::PySet;
@@ -43,6 +43,8 @@ use crate::vm::VirtualMachine;
// just to avoid unused import warnings
#[cfg(unix)]
use crate::obj::objdict::PyMapping;
#[cfg(unix)]
use crate::pyobject::PyIterable;
#[cfg(unix)]
use std::convert::TryFrom;
@@ -119,12 +121,12 @@ enum OutputMode {
fn output_by_mode(val: String, mode: OutputMode, vm: &VirtualMachine) -> PyObjectRef {
match mode {
OutputMode::String => vm.ctx.new_str(val),
OutputMode::Bytes => vm.ctx.new_bytes(val.as_bytes().to_vec()),
OutputMode::Bytes => vm.ctx.new_bytes(val.into_bytes()),
}
}
pub struct PyPathLike {
path: ffi::OsString,
pub path: ffi::OsString,
mode: OutputMode,
}
@@ -1036,6 +1038,19 @@ fn os_get_inheritable(fd: RawFd, vm: &VirtualMachine) -> PyResult<bool> {
}
}
#[cfg(unix)]
pub(crate) fn set_inheritable(fd: RawFd, inheritable: bool) -> nix::Result<()> {
use nix::fcntl;
let flags = fcntl::FdFlag::from_bits_truncate(fcntl::fcntl(fd, fcntl::FcntlArg::F_GETFD)?);
let mut new_flags = flags;
new_flags.set(fcntl::FdFlag::FD_CLOEXEC, !inheritable);
if flags != new_flags {
fcntl::fcntl(fd, fcntl::FcntlArg::F_SETFD(new_flags))?;
}
Ok(())
}
fn os_set_inheritable(fd: i64, inheritable: bool, vm: &VirtualMachine) -> PyResult<()> {
#[cfg(not(any(unix, windows)))]
{
@@ -1043,21 +1058,7 @@ fn os_set_inheritable(fd: i64, inheritable: bool, vm: &VirtualMachine) -> PyResu
}
#[cfg(unix)]
{
let fd = fd as RawFd;
let _set_flag = || {
use nix::fcntl::fcntl;
use nix::fcntl::FcntlArg;
use nix::fcntl::FdFlag;
let flags = FdFlag::from_bits_truncate(fcntl(fd, FcntlArg::F_GETFD)?);
let mut new_flags = flags;
new_flags.set(FdFlag::from_bits_truncate(libc::FD_CLOEXEC), !inheritable);
if flags != new_flags {
fcntl(fd, FcntlArg::F_SETFD(new_flags))?;
}
Ok(())
};
_set_flag().or_else(|err| Err(convert_nix_error(vm, err)))
set_inheritable(fd as RawFd, inheritable).map_err(|err| convert_nix_error(vm, err))
}
#[cfg(windows)]
{
@@ -1860,6 +1861,12 @@ fn os_kill(pid: i32, sig: isize, vm: &VirtualMachine) -> PyResult<()> {
}
}
fn os_strerror(e: i32) -> String {
unsafe { ffi::CStr::from_ptr(libc::strerror(e)) }
.to_string_lossy()
.into_owned()
}
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
let ctx = &vm.ctx;
@@ -1970,6 +1977,7 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
"set_inheritable" => ctx.new_function(os_set_inheritable),
"link" => ctx.new_function(os_link),
"kill" => ctx.new_function(os_kill),
"strerror" => ctx.new_function(os_strerror),
"O_RDONLY" => ctx.new_int(libc::O_RDONLY),
"O_WRONLY" => ctx.new_int(libc::O_WRONLY),
+204
View File
@@ -0,0 +1,204 @@
use nix::{dir, errno::Errno, fcntl, unistd};
use std::convert::Infallible as Never;
use std::ffi::{CStr, CString};
use std::io::{self, prelude::*};
use std::os::unix::ffi::OsStringExt;
use crate::pyobject::{PyObjectRef, PyResult, PySequence, TryFromObject};
use crate::VirtualMachine;
use super::os::{convert_nix_error, set_inheritable, PyPathLike};
macro_rules! gen_args {
($($field:ident: $t:ty),*$(,)?) => {
#[derive(FromArgs)]
struct ForkExecArgs {
$(#[pyarg(positional_only)] $field: $t,)*
}
};
}
struct CStrPathLike {
s: CString,
}
impl TryFromObject for CStrPathLike {
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
let s = PyPathLike::try_from_object(vm, obj)?.path.into_vec();
let s = CString::new(s)
.map_err(|_| vm.new_value_error("embedded null character".to_owned()))?;
Ok(CStrPathLike { s })
}
}
gen_args! {
args: PySequence<CStrPathLike> /* list */, exec_list: PySequence<CStrPathLike> /* list */,
close_fds: bool, fds_to_keep: PySequence<i32>,
cwd: Option<CStrPathLike>, env_list: Option<PySequence<CStrPathLike>>,
p2cread: i32, p2cwrite: i32, c2pread: i32, c2pwrite: i32,
errread: i32, errwrite: i32, errpipe_read: i32, errpipe_write: i32,
restore_signals: bool, call_setsid: bool, preexec_fn: Option<PyObjectRef>,
}
fn _posixsubprocess_fork_exec(args: ForkExecArgs, vm: &VirtualMachine) -> PyResult<libc::pid_t> {
if args.preexec_fn.is_some() {
return Err(vm.new_not_implemented_error("preexec_fn not supported yet".to_owned()));
}
let cstrs_to_ptrs = |cstrs: &[CStrPathLike]| {
cstrs
.iter()
.map(|s| s.s.as_ptr())
.chain(std::iter::once(std::ptr::null()))
.collect::<Vec<_>>()
};
let argv = cstrs_to_ptrs(args.args.as_slice());
let argv = &argv;
let envp = args.env_list.as_ref().map(|s| cstrs_to_ptrs(s.as_slice()));
let envp = envp.as_deref();
match nix::unistd::fork().map_err(|e| convert_nix_error(vm, e))? {
nix::unistd::ForkResult::Child => exec(&args, ProcArgs { argv, envp }),
nix::unistd::ForkResult::Parent { child } => Ok(child.as_raw()),
}
}
// can't reallocate inside of exec(), so we reallocate prior to fork() and pass this along
struct ProcArgs<'a> {
argv: &'a [*const libc::c_char],
envp: Option<&'a [*const libc::c_char]>,
}
fn exec(args: &ForkExecArgs, procargs: ProcArgs) -> ! {
match exec_inner(args, procargs) {
Ok(x) => match x {},
Err(e) => {
let e = e.as_errno().expect("got a non-errno nix error");
let buf: &mut [u8] = &mut [0; 256];
let mut cur = io::Cursor::new(&mut *buf);
// TODO: check if reached preexec, if not then have "noexec" after
let _ = write!(cur, "OSError:{}:", e as i32);
let pos = cur.position();
let _ = unistd::write(args.errpipe_write, &buf[..pos as usize]);
std::process::exit(255)
}
}
}
fn exec_inner(args: &ForkExecArgs, procargs: ProcArgs) -> nix::Result<Never> {
for &fd in args.fds_to_keep.as_slice() {
if fd != args.errpipe_write {
set_inheritable(fd, true)?
}
}
for &fd in &[args.p2cwrite, args.c2pread, args.errread] {
if fd != -1 {
unistd::close(fd)?;
}
}
unistd::close(args.errpipe_read)?;
let c2pwrite = if args.c2pwrite == 0 {
let fd = unistd::dup(args.c2pwrite)?;
set_inheritable(fd, true)?;
fd
} else {
args.c2pwrite
};
let mut errwrite = args.errwrite;
while errwrite == 0 || errwrite == 1 {
errwrite = unistd::dup(errwrite)?;
set_inheritable(errwrite, true)?;
}
let dup_into_stdio = |fd, io_fd| {
if fd == io_fd {
set_inheritable(fd, true)
} else if fd != -1 {
unistd::dup2(fd, io_fd).map(drop)
} else {
Ok(())
}
};
dup_into_stdio(args.p2cread, 0)?;
dup_into_stdio(c2pwrite, 1)?;
dup_into_stdio(errwrite, 2)?;
if let Some(ref cwd) = args.cwd {
unistd::chdir(cwd.s.as_c_str())?
}
if args.restore_signals {
// TODO: restore signals SIGPIPE, SIGXFZ, SIGXFSZ to SIG_DFL
}
if args.call_setsid {
unistd::setsid()?;
}
if args.close_fds {
close_fds(3, args.fds_to_keep.as_slice())?;
}
let mut first_err = None;
for exec in args.exec_list.as_slice() {
if let Some(envp) = procargs.envp {
unsafe { libc::execve(exec.s.as_ptr(), procargs.argv.as_ptr(), envp.as_ptr()) };
} else {
unsafe { libc::execv(exec.s.as_ptr(), procargs.argv.as_ptr()) };
}
let e = Errno::last();
if e != Errno::ENOENT && e != Errno::ENOTDIR && first_err.is_none() {
first_err = Some(e)
}
}
Err(first_err.unwrap_or_else(Errno::last).into())
}
fn close_fds(above: i32, keep: &[i32]) -> nix::Result<()> {
// TODO: close fds by brute force if readdir doesn't work:
// https://github.com/python/cpython/blob/3.8/Modules/_posixsubprocess.c#L220
let path = unsafe { CStr::from_bytes_with_nul_unchecked(FD_DIR_NAME) };
let mut dir = dir::Dir::open(
path,
fcntl::OFlag::O_RDONLY | fcntl::OFlag::O_DIRECTORY,
nix::sys::stat::Mode::empty(),
)?;
for e in dir.iter() {
if let Some(fd) = pos_int_from_ascii(e?.file_name()) {
if fd > above && !keep.contains(&fd) {
unistd::close(fd)?
}
}
}
Ok(())
}
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "macos",
))]
const FD_DIR_NAME: &[u8] = b"/dev/fd\0";
#[cfg(target_os = "linux")]
const FD_DIR_NAME: &[u8] = b"/proc/self/fd\0";
fn pos_int_from_ascii(name: &CStr) -> Option<i32> {
let mut num = 0;
for c in name.to_bytes() {
if !c.is_ascii_digit() {
return None;
}
num = num * 10 + i32::from(c - b'0')
}
Some(num)
}
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
let ctx = &vm.ctx;
py_module!(vm, "_posixsubprocess", {
"fork_exec" => named_function!(ctx, _posixsubprocess, fork_exec),
})
}
-295
View File
@@ -1,295 +0,0 @@
use std::ffi::OsString;
use std::fs::File;
use std::io::ErrorKind;
use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use std::time::Duration;
use crate::function::OptionalArg;
use crate::obj::objbytes::PyBytesRef;
use crate::obj::objlist::PyListRef;
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::{convert_io_error, raw_file_number, rust_file};
use crate::vm::VirtualMachine;
#[derive(Debug)]
struct Popen {
process: RwLock<subprocess::Popen>,
args: PyObjectRef,
}
// Remove once https://github.com/hniksic/rust-subprocess/issues/42 is resolved
#[cfg(windows)]
unsafe impl Sync for Popen {}
impl PyValue for Popen {
fn class(vm: &VirtualMachine) -> PyClassRef {
vm.class("_subprocess", "Popen")
}
}
type PopenRef = PyRef<Popen>;
#[derive(FromArgs)]
#[allow(dead_code)]
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>,
#[pyarg(positional_or_keyword, default = "None")]
close_fds: Option<bool>, // TODO: use these unused options
#[pyarg(positional_or_keyword, default = "None")]
cwd: Option<PyStringRef>,
#[pyarg(positional_or_keyword, default = "None")]
start_new_session: Option<bool>,
}
#[derive(FromArgs)]
struct PopenWaitArgs {
#[pyarg(positional_or_keyword, default = "None")]
timeout: Option<u64>,
}
impl IntoPyObject for subprocess::ExitStatus {
fn into_pyobject(self, vm: &VirtualMachine) -> PyResult {
let status: i32 = match self {
subprocess::ExitStatus::Exited(status) => status as i32,
subprocess::ExitStatus::Signaled(status) => -i32::from(status),
subprocess::ExitStatus::Other(status) => status as i32,
_ => return Err(vm.new_os_error("Unknown exist status".to_owned())),
};
Ok(vm.new_int(status))
}
}
#[cfg(windows)]
const NULL_DEVICE: &str = "nul";
#[cfg(unix)]
const NULL_DEVICE: &str = "/dev/null";
fn convert_redirection(arg: Option<i64>, vm: &VirtualMachine) -> PyResult<subprocess::Redirection> {
match arg {
Some(fd) => match fd {
-1 => Ok(subprocess::Redirection::Pipe),
-2 => Ok(subprocess::Redirection::Merge),
-3 => Ok(subprocess::Redirection::File(
File::open(NULL_DEVICE).unwrap(),
)),
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),
}
}
fn convert_to_file_io(file: &Option<File>, mode: &str, vm: &VirtualMachine) -> PyResult {
match file {
Some(ref stdin) => io_open(
vm.new_int(raw_file_number(stdin.try_clone().unwrap())),
Some(mode),
Default::default(),
vm,
),
None => Ok(vm.get_none()),
}
}
impl PopenRef {
fn borrow_process(&self) -> RwLockReadGuard<'_, subprocess::Popen> {
self.process.read().unwrap()
}
fn borrow_process_mut(&self) -> RwLockWriteGuard<'_, subprocess::Popen> {
self.process.write().unwrap()
}
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_owned()],
Either::B(command_list) => command_list
.borrow_elements()
.iter()
.map(|x| objstr::clone_value(x))
.collect(),
};
let cwd = args.cwd.map(|x| OsString::from(x.as_str()));
let process = subprocess::Popen::create(
&command_list,
subprocess::PopenConfig {
stdin,
stdout,
stderr,
cwd,
..Default::default()
},
)
.map_err(|s| vm.new_os_error(format!("Could not start program: {}", s)))?;
Popen {
process: RwLock::new(process),
args: args.args.into_object(),
}
.into_ref_with_type(vm, cls)
}
fn poll(self) -> Option<subprocess::ExitStatus> {
self.borrow_process_mut().poll()
}
fn return_code(self) -> Option<subprocess::ExitStatus> {
self.borrow_process().exit_status()
}
fn wait(self, args: PopenWaitArgs, vm: &VirtualMachine) -> PyResult<i64> {
let timeout = match args.timeout {
Some(timeout) => self
.borrow_process_mut()
.wait_timeout(Duration::new(timeout, 0)),
None => self.borrow_process_mut().wait().map(Some),
}
.map_err(|s| vm.new_os_error(format!("Could not start program: {}", s)))?;
if let Some(exit) = timeout {
use subprocess::ExitStatus::*;
Ok(match exit {
Exited(i) => i.into(),
Signaled(s) => -i64::from(s),
_ => unreachable!("should not occur in normal operation"),
})
} else {
let timeout_expired = vm.try_class("_subprocess", "TimeoutExpired")?;
Err(vm.new_exception_msg(timeout_expired, "Timeout".to_owned()))
}
}
fn stdin(self, vm: &VirtualMachine) -> PyResult {
convert_to_file_io(&self.borrow_process().stdin, "wb", vm)
}
fn stdout(self, vm: &VirtualMachine) -> PyResult {
convert_to_file_io(&self.borrow_process().stdout, "rb", vm)
}
fn stderr(self, vm: &VirtualMachine) -> PyResult {
convert_to_file_io(&self.borrow_process().stderr, "rb", vm)
}
fn terminate(self, vm: &VirtualMachine) -> PyResult<()> {
self.borrow_process_mut()
.terminate()
.map_err(|err| convert_io_error(vm, err))
}
fn kill(self, vm: &VirtualMachine) -> PyResult<()> {
self.borrow_process_mut()
.kill()
.map_err(|err| convert_io_error(vm, err))
}
#[allow(clippy::type_complexity)]
fn communicate(
self,
args: PopenCommunicateArgs,
vm: &VirtualMachine,
) -> PyResult<(Option<Vec<u8>>, Option<Vec<u8>>)> {
let bytes = match args.input {
OptionalArg::Present(ref bytes) => Some(bytes.get_value().to_vec()),
OptionalArg::Missing => None,
};
let mut communicator = self.borrow_process_mut().communicate_start(bytes);
if let OptionalArg::Present(timeout) = args.timeout {
communicator = communicator.limit_time(Duration::new(timeout, 0));
}
communicator.read().map_err(|err| {
if err.error.kind() == ErrorKind::TimedOut {
let timeout_expired = vm.try_class("_subprocess", "TimeoutExpired").unwrap();
vm.new_exception_msg(timeout_expired, "Timeout".to_owned())
} else {
convert_io_error(vm, err.error)
}
})
}
fn pid(self) -> Option<u32> {
self.borrow_process().pid()
}
fn enter(self) -> Self {
self
}
fn exit(
self,
_exception_type: PyObjectRef,
_exception_value: PyObjectRef,
_traceback: PyObjectRef,
) {
let mut process = self.borrow_process_mut();
process.stdout.take();
process.stdin.take();
process.stderr.take();
}
fn args(self) -> PyObjectRef {
self.args.clone()
}
}
#[derive(FromArgs)]
#[allow(dead_code)]
struct PopenCommunicateArgs {
#[pyarg(positional_or_keyword, optional = true)]
input: OptionalArg<PyBytesRef>,
#[pyarg(positional_or_keyword, optional = true)]
timeout: OptionalArg<u64>,
}
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
let ctx = &vm.ctx;
let subprocess_error = ctx.new_class("SubprocessError", ctx.exceptions.exception_type.clone());
let timeout_expired = ctx.new_class("TimeoutExpired", subprocess_error.clone());
let called_process_error = ctx.new_class("CalledProcessError", subprocess_error.clone());
let popen = py_class!(ctx, "Popen", ctx.object(), {
(slot new) => PopenRef::new,
"poll" => ctx.new_method(PopenRef::poll),
"returncode" => ctx.new_readonly_getset("returncode", PopenRef::return_code),
"wait" => ctx.new_method(PopenRef::wait),
"stdin" => ctx.new_readonly_getset("stdin", PopenRef::stdin),
"stdout" => ctx.new_readonly_getset("stdout", PopenRef::stdout),
"stderr" => ctx.new_readonly_getset("stderr", PopenRef::stderr),
"terminate" => ctx.new_method(PopenRef::terminate),
"kill" => ctx.new_method(PopenRef::kill),
"communicate" => ctx.new_method(PopenRef::communicate),
"pid" => ctx.new_readonly_getset("pid", PopenRef::pid),
"__enter__" => ctx.new_method(PopenRef::enter),
"__exit__" => ctx.new_method(PopenRef::exit),
"args" => ctx.new_readonly_getset("args", PopenRef::args),
});
py_module!(vm, "_subprocess", {
"Popen" => popen,
"SubprocessError" => subprocess_error,
"TimeoutExpired" => timeout_expired,
"CalledProcessError" => called_process_error,
"PIPE" => ctx.new_int(-1),
"STDOUT" => ctx.new_int(-2),
"DEVNULL" => ctx.new_int(-3),
})
}
+16
View File
@@ -0,0 +1,16 @@
use crate::pyobject::{ItemProtocol, PyObjectRef};
use crate::VirtualMachine;
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
let vars = vm.ctx.new_dict();
macro_rules! hashmap {
($($key:literal => $value:literal),*) => {{
$(vars.set_item($key, vm.new_str($value.to_owned()), vm).unwrap();)*
}};
}
include!(concat!(env!("OUT_DIR"), "/env_vars.rs"));
py_module!(vm, "_sysconfigdata", {
"build_time_vars" => vars,
})
}
+373 -10
View File
@@ -1,30 +1,393 @@
#![allow(non_snake_case)]
use std::ptr::{null, null_mut};
use winapi::shared::winerror;
use winapi::um::winnt::HANDLE;
use winapi::um::{handleapi, winbase};
use winapi::um::{
fileapi, handleapi, namedpipeapi, processenv, processthreadsapi, synchapi, winbase, winnt,
winuser,
};
use super::os;
use crate::pyobject::{PyObjectRef, PyResult};
use super::os::errno_err;
use crate::function::OptionalArg;
use crate::obj::objdict::{PyDictRef, PyMapping};
use crate::obj::objstr::PyStringRef;
use crate::pyobject::{PyObjectRef, PyResult, PySequence, TryFromObject};
use crate::VirtualMachine;
fn winapi_CloseHandle(handle: usize, vm: &VirtualMachine) -> PyResult<()> {
let res = unsafe { handleapi::CloseHandle(handle as HANDLE) };
if res == 0 {
Err(os::errno_err(vm))
} else {
Ok(())
fn GetLastError() -> u32 {
unsafe { winapi::um::errhandlingapi::GetLastError() }
}
fn husize(h: HANDLE) -> usize {
h as usize
}
trait Convertable {
fn is_err(&self) -> bool;
}
impl Convertable for HANDLE {
fn is_err(&self) -> bool {
*self == handleapi::INVALID_HANDLE_VALUE
}
}
impl Convertable for i32 {
fn is_err(&self) -> bool {
*self == 0
}
}
fn cvt<T: Convertable>(vm: &VirtualMachine, res: T) -> PyResult<T> {
if res.is_err() {
Err(errno_err(vm))
} else {
Ok(res)
}
}
fn _winapi_CloseHandle(handle: usize, vm: &VirtualMachine) -> PyResult<()> {
cvt(vm, unsafe { handleapi::CloseHandle(handle as HANDLE) }).map(drop)
}
fn _winapi_GetStdHandle(std_handle: u32, vm: &VirtualMachine) -> PyResult<usize> {
cvt(vm, unsafe { processenv::GetStdHandle(std_handle) }).map(husize)
}
fn _winapi_CreatePipe(
_pipe_attrs: PyObjectRef,
size: u32,
vm: &VirtualMachine,
) -> PyResult<(usize, usize)> {
let mut read = null_mut();
let mut write = null_mut();
cvt(vm, unsafe {
namedpipeapi::CreatePipe(&mut read, &mut write, null_mut(), size)
})?;
Ok((read as usize, write as usize))
}
fn _winapi_DuplicateHandle(
(src_process, src): (usize, usize),
target_process: usize,
access: u32,
inherit: i32,
options: OptionalArg<u32>,
vm: &VirtualMachine,
) -> PyResult<usize> {
let mut target = null_mut();
cvt(vm, unsafe {
handleapi::DuplicateHandle(
src_process as _,
src as _,
target_process as _,
&mut target,
access,
inherit,
options.unwrap_or(0),
)
})?;
Ok(target as usize)
}
fn _winapi_GetCurrentProcess() -> usize {
unsafe { processthreadsapi::GetCurrentProcess() as usize }
}
fn _winapi_GetFileType(h: usize, vm: &VirtualMachine) -> PyResult<u32> {
let ret = unsafe { fileapi::GetFileType(h as _) };
if ret == 0 && GetLastError() != 0 {
Err(errno_err(vm))
} else {
Ok(ret)
}
}
#[derive(FromArgs)]
struct CreateProcessArgs {
#[pyarg(positional_only)]
name: Option<PyStringRef>,
#[pyarg(positional_only)]
command_line: Option<PyStringRef>,
#[pyarg(positional_only)]
_proc_attrs: PyObjectRef,
#[pyarg(positional_only)]
_thread_attrs: PyObjectRef,
#[pyarg(positional_only)]
inherit_handles: i32,
#[pyarg(positional_only)]
creation_flags: u32,
#[pyarg(positional_only)]
env_mapping: Option<PyMapping>,
#[pyarg(positional_only)]
current_dir: Option<PyStringRef>,
#[pyarg(positional_only)]
startup_info: PyObjectRef,
}
fn _winapi_CreateProcess(
args: CreateProcessArgs,
vm: &VirtualMachine,
) -> PyResult<(usize, usize, u32, u32)> {
use winbase::STARTUPINFOEXW;
let mut si: STARTUPINFOEXW = unsafe { std::mem::zeroed() };
si.StartupInfo.cb = std::mem::size_of::<STARTUPINFOEXW>() as _;
macro_rules! si_attr {
($attr:ident, $t:ty) => {{
si.StartupInfo.$attr = <Option<$t>>::try_from_object(
vm,
vm.get_attribute(args.startup_info.clone(), stringify!($attr))?,
)?
.unwrap_or(0) as _
}};
($attr:ident) => {{
si.StartupInfo.$attr = <Option<_>>::try_from_object(
vm,
vm.get_attribute(args.startup_info.clone(), stringify!($attr))?,
)?
.unwrap_or(0)
}};
}
si_attr!(dwFlags);
si_attr!(wShowWindow);
si_attr!(hStdInput, usize);
si_attr!(hStdOutput, usize);
si_attr!(hStdError, usize);
let mut env = args
.env_mapping
.map(|m| getenvironment(m.into_dict(), vm))
.transpose()?;
let env = env.as_mut().map_or_else(null_mut, |v| v.as_mut_ptr());
let mut attrlist = getattributelist(
vm.get_attribute(args.startup_info.clone(), "lpAttributeList")?,
vm,
)?;
si.lpAttributeList = attrlist
.as_mut()
.map_or_else(null_mut, |l| l.attrlist.as_mut_ptr() as _);
let wstr = |s: PyStringRef| {
if s.as_str().contains('\0') {
Err(vm.new_value_error("embedded null character".to_owned()))
} else {
Ok(s.as_str()
.encode_utf16()
.chain(std::iter::once(0))
.collect::<Vec<_>>())
}
};
let app_name = args.name.map(wstr).transpose()?;
let app_name = app_name.as_ref().map_or_else(null, |w| w.as_ptr());
let mut command_line = args.command_line.map(wstr).transpose()?;
let command_line = command_line
.as_mut()
.map_or_else(null_mut, |w| w.as_mut_ptr());
let mut current_dir = args.current_dir.map(wstr).transpose()?;
let current_dir = current_dir
.as_mut()
.map_or_else(null_mut, |w| w.as_mut_ptr());
let mut procinfo = unsafe { std::mem::zeroed() };
let ret = unsafe {
processthreadsapi::CreateProcessW(
app_name,
command_line,
null_mut(),
null_mut(),
args.inherit_handles,
args.creation_flags
| winbase::EXTENDED_STARTUPINFO_PRESENT
| winbase::CREATE_UNICODE_ENVIRONMENT,
env as _,
current_dir,
&mut si as *mut STARTUPINFOEXW as _,
&mut procinfo,
)
};
if ret == 0 {
return Err(errno_err(vm));
}
Ok((
procinfo.hProcess as usize,
procinfo.hThread as usize,
procinfo.dwProcessId,
procinfo.dwThreadId,
))
}
fn getenvironment(env: PyDictRef, vm: &VirtualMachine) -> PyResult<Vec<u16>> {
let mut out = vec![];
for (k, v) in env {
let k = PyStringRef::try_from_object(vm, k)?;
let k = k.as_str();
let v = PyStringRef::try_from_object(vm, v)?;
let v = v.as_str();
if k.contains('\0') || v.contains('\0') {
return Err(vm.new_value_error("embedded null character".to_owned()));
}
if k.len() == 0 || k[1..].contains('=') {
return Err(vm.new_value_error("illegal environment variable name".to_owned()));
}
out.extend(k.encode_utf16());
out.push(b'=' as u16);
out.extend(v.encode_utf16());
out.push(b'\0' as u16);
}
out.push(b'\0' as u16);
Ok(out)
}
struct AttrList {
handlelist: Option<Vec<usize>>,
attrlist: Vec<u8>,
}
impl Drop for AttrList {
fn drop(&mut self) {
unsafe {
processthreadsapi::DeleteProcThreadAttributeList(self.attrlist.as_mut_ptr() as _)
};
}
}
fn getattributelist(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<Option<AttrList>> {
<Option<PyMapping>>::try_from_object(vm, obj)?
.map(|d| {
let d = d.into_dict();
let handlelist = d
.get_item_option("handle_list", vm)?
.and_then(|obj| {
<Option<PySequence<usize>>>::try_from_object(vm, obj)
.and_then(|s| match s {
Some(s) if !s.as_slice().is_empty() => Ok(Some(s.into_vec())),
_ => Ok(None),
})
.transpose()
})
.transpose()?;
let attr_count = handlelist.is_some() as u32;
let mut size = 0;
let ret = unsafe {
processthreadsapi::InitializeProcThreadAttributeList(
null_mut(),
attr_count,
0,
&mut size,
)
};
if ret != 0 || GetLastError() != winerror::ERROR_INSUFFICIENT_BUFFER {
return Err(errno_err(vm));
}
let mut attrlist = vec![0u8; size];
let ret = unsafe {
processthreadsapi::InitializeProcThreadAttributeList(
attrlist.as_mut_ptr() as _,
attr_count,
0,
&mut size,
)
};
if ret == 0 {
return Err(errno_err(vm));
}
let mut attrs = AttrList {
handlelist,
attrlist,
};
if let Some(ref mut handlelist) = attrs.handlelist {
let ret = unsafe {
processthreadsapi::UpdateProcThreadAttribute(
attrs.attrlist.as_mut_ptr() as _,
0,
(2 & 0xffff) | 0x20000, // PROC_THREAD_ATTRIBUTE_HANDLE_LIST
handlelist.as_mut_ptr() as _,
(handlelist.len() * std::mem::size_of::<HANDLE>()) as _,
null_mut(),
null_mut(),
)
};
if ret == 0 {
return Err(errno_err(vm));
}
}
Ok(attrs)
})
.transpose()
}
fn _winapi_WaitForSingleObject(h: usize, ms: u32, vm: &VirtualMachine) -> PyResult<u32> {
let ret = unsafe { synchapi::WaitForSingleObject(h as _, ms) };
if ret == winbase::WAIT_FAILED {
Err(errno_err(vm))
} else {
Ok(ret)
}
}
fn _winapi_GetExitCodeProcess(h: usize, vm: &VirtualMachine) -> PyResult<u32> {
let mut ec = 0;
cvt(vm, unsafe {
processthreadsapi::GetExitCodeProcess(h as _, &mut ec)
})?;
Ok(ec)
}
fn _winapi_TerminateProcess(h: usize, exit_code: u32, vm: &VirtualMachine) -> PyResult<()> {
cvt(vm, unsafe {
processthreadsapi::TerminateProcess(h as _, exit_code)
})
.map(drop)
}
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
let ctx = &vm.ctx;
py_module!(vm, "_winapi", {
"CloseHandle" => ctx.new_function(winapi_CloseHandle),
"CloseHandle" => named_function!(ctx, _winapi, CloseHandle),
"GetStdHandle" => named_function!(ctx, _winapi, GetStdHandle),
"CreatePipe" => named_function!(ctx, _winapi, CreatePipe),
"DuplicateHandle" => named_function!(ctx, _winapi, DuplicateHandle),
"GetCurrentProcess" => named_function!(ctx, _winapi, GetCurrentProcess),
"CreateProcess" => named_function!(ctx, _winapi, CreateProcess),
"WaitForSingleObject" => named_function!(ctx, _winapi, WaitForSingleObject),
"GetExitCodeProcess" => named_function!(ctx, _winapi, GetExitCodeProcess),
"TerminateProcess" => named_function!(ctx, _winapi, TerminateProcess),
"WAIT_OBJECT_0" => ctx.new_int(winbase::WAIT_OBJECT_0),
"WAIT_ABANDONED" => ctx.new_int(winbase::WAIT_ABANDONED),
"WAIT_ABANDONED_0" => ctx.new_int(winbase::WAIT_ABANDONED_0),
"WAIT_TIMEOUT" => ctx.new_int(winerror::WAIT_TIMEOUT),
"INFINITE" => ctx.new_int(winbase::INFINITE),
"CREATE_NEW_CONSOLE" => ctx.new_int(winbase::CREATE_NEW_CONSOLE),
"CREATE_NEW_PROCESS_GROUP" => ctx.new_int(winbase::CREATE_NEW_PROCESS_GROUP),
"STD_INPUT_HANDLE" => ctx.new_int(winbase::STD_INPUT_HANDLE),
"STD_OUTPUT_HANDLE" => ctx.new_int(winbase::STD_OUTPUT_HANDLE),
"STD_ERROR_HANDLE" => ctx.new_int(winbase::STD_ERROR_HANDLE),
"SW_HIDE" => ctx.new_int(winuser::SW_HIDE),
"STARTF_USESTDHANDLES" => ctx.new_int(winbase::STARTF_USESTDHANDLES),
"STARTF_USESHOWWINDOW" => ctx.new_int(winbase::STARTF_USESHOWWINDOW),
"ABOVE_NORMAL_PRIORITY_CLASS" => ctx.new_int(winbase::ABOVE_NORMAL_PRIORITY_CLASS),
"BELOW_NORMAL_PRIORITY_CLASS" => ctx.new_int(winbase::BELOW_NORMAL_PRIORITY_CLASS),
"HIGH_PRIORITY_CLASS" => ctx.new_int(winbase::HIGH_PRIORITY_CLASS),
"IDLE_PRIORITY_CLASS" => ctx.new_int(winbase::IDLE_PRIORITY_CLASS),
"NORMAL_PRIORITY_CLASS" => ctx.new_int(winbase::NORMAL_PRIORITY_CLASS),
"REALTIME_PRIORITY_CLASS" => ctx.new_int(winbase::REALTIME_PRIORITY_CLASS),
"CREATE_NO_WINDOW" => ctx.new_int(winbase::CREATE_NO_WINDOW),
"DETACHED_PROCESS" => ctx.new_int(winbase::DETACHED_PROCESS),
"CREATE_DEFAULT_ERROR_MODE" => ctx.new_int(winbase::CREATE_DEFAULT_ERROR_MODE),
"CREATE_BREAKAWAY_FROM_JOB" => ctx.new_int(winbase::CREATE_BREAKAWAY_FROM_JOB),
"DUPLICATE_SAME_ACCESS" => ctx.new_int(winnt::DUPLICATE_SAME_ACCESS),
"FILE_TYPE_CHAR" => ctx.new_int(winbase::FILE_TYPE_CHAR),
"FILE_TYPE_DISK" => ctx.new_int(winbase::FILE_TYPE_DISK),
"FILE_TYPE_PIPE" => ctx.new_int(winbase::FILE_TYPE_PIPE),
"FILE_TYPE_REMOTE" => ctx.new_int(winbase::FILE_TYPE_REMOTE),
"FILE_TYPE_UNKNOWN" => ctx.new_int(winbase::FILE_TYPE_UNKNOWN),
})
}
+29 -15
View File
@@ -222,6 +222,32 @@ fn sys_displayhook(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
Ok(())
}
const PLATFORM: &str = {
cfg_if::cfg_if! {
if #[cfg(any(target_os = "linux", target_os = "android"))] {
// Android is linux as well. see https://bugs.python.org/issue32637
"linux"
} else if #[cfg(target_os = "macos")] {
"darwin"
} else if #[cfg(windows)] {
"win32"
} else {
"unknown"
}
}
};
const ABIFLAGS: &str = "";
// not the same as CPython (e.g. rust's x86_x64-unknown-linux-gnu is just x86_64-linux-gnu)
// but hopefully that's just an implementation detail? TODO: copy CPython's multiarch exactly,
// https://github.com/python/cpython/blob/3.8/configure.ac#L725
const MULTIARCH: &str = env!("RUSTPYTHON_TARGET_TRIPLE");
pub fn sysconfigdata_name() -> String {
format!("_sysconfigdata_{}_{}_{}", ABIFLAGS, PLATFORM, MULTIARCH)
}
pub fn make_module(vm: &VirtualMachine, module: PyObjectRef, builtins: PyObjectRef) {
let ctx = &vm.ctx;
@@ -244,6 +270,7 @@ pub fn make_module(vm: &VirtualMachine, module: PyObjectRef, builtins: PyObjectR
let implementation = py_namespace!(vm, {
"name" => ctx.new_str("rustpython".to_owned()),
"cache_tag" => ctx.new_str("rustpython-01".to_owned()),
"_multiarch" => ctx.new_str(MULTIARCH.to_owned()),
});
let path = ctx.new_list(
@@ -255,19 +282,6 @@ pub fn make_module(vm: &VirtualMachine, module: PyObjectRef, builtins: PyObjectR
.collect(),
);
let platform = if cfg!(target_os = "linux") {
"linux".to_owned()
} else if cfg!(target_os = "macos") {
"darwin".to_owned()
} else if cfg!(target_os = "windows") {
"win32".to_owned()
} else if cfg!(target_os = "android") {
// Linux as well. see https://bugs.python.org/issue32637
"linux".to_owned()
} else {
"unknown".to_owned()
};
let framework = "".to_owned();
// https://doc.rust-lang.org/reference/conditional-compilation.html#target_endian
@@ -395,7 +409,7 @@ settrace() -- set the global debug tracing function
"_getframe" => ctx.new_function(getframe),
"modules" => modules.clone(),
"warnoptions" => ctx.new_list(vec![]),
"platform" => ctx.new_str(platform),
"platform" => ctx.new_str(PLATFORM.to_owned()),
"_framework" => ctx.new_str(framework),
"meta_path" => ctx.new_list(vec![]),
"path_hooks" => ctx.new_list(vec![]),
@@ -414,7 +428,7 @@ settrace() -- set the global debug tracing function
"exec_prefix" => ctx.new_str(exec_prefix.to_owned()),
"base_exec_prefix" => ctx.new_str(base_exec_prefix.to_owned()),
"exit" => ctx.new_function(sys_exit),
"abiflags" => ctx.new_str("".to_owned()),
"abiflags" => ctx.new_str(ABIFLAGS.to_owned()),
"audit" => ctx.new_function(sys_audit),
"displayhook" => ctx.new_function(sys_displayhook),
"__displayhook__" => ctx.new_function(sys_displayhook),