From 84bdd8d4e39c9d8b53df35fd45d27567d99866c7 Mon Sep 17 00:00:00 2001 From: Daniel Stuart Date: Wed, 2 Oct 2024 22:41:23 -0300 Subject: [PATCH 1/2] Make stdio a feature When embedding RustPython into a graphical windows application (where windows_subsystem = "windows"), it fails to create the RustPython VM. This happens because the VM will try to open stdin/stdout/stderr when initializing, but stdio isn't available for /SUBSYSTEM:WINDOWS apps. This commit makes stdio a feature, so it can be disabled when needed. https://doc.rust-lang.org/reference/runtime.html#the-windows_subsystem-attribute https://learn.microsoft.com/en-us/cpp/build/reference/subsystem-specify-subsystem?view=msvc-170 https://asawicki.info/news_1768_ways_to_print_and_capture_text_output_of_a_process --- Cargo.toml | 3 ++- vm/Cargo.toml | 3 ++- vm/src/vm/mod.rs | 42 +++++++++++++++++++++++------------------- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6e90643efe2..879bb4e75ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,9 +10,10 @@ repository.workspace = true license.workspace = true [features] -default = ["threading", "stdlib", "zlib", "importlib"] +default = ["threading", "stdlib", "stdio", "zlib", "importlib"] importlib = ["rustpython-vm/importlib"] encodings = ["rustpython-vm/encodings"] +stdio = ["rustpython-vm/stdio"] stdlib = ["rustpython-stdlib", "rustpython-pylib", "encodings"] flame-it = ["rustpython-vm/flame-it", "flame", "flamescope"] freeze-stdlib = ["stdlib", "rustpython-vm/freeze-stdlib", "rustpython-pylib?/freeze-stdlib"] diff --git a/vm/Cargo.toml b/vm/Cargo.toml index 4bcd8512e20..e0ca09f3329 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -10,7 +10,8 @@ repository.workspace = true license.workspace = true [features] -default = ["compiler", "wasmbind"] +default = ["compiler", "wasmbind", "stdio"] +stdio = [] importlib = [] encodings = ["importlib"] vm-tracing-logging = [] diff --git a/vm/src/vm/mod.rs b/vm/src/vm/mod.rs index 1a5f40e767f..5c3b386e524 100644 --- a/vm/src/vm/mod.rs +++ b/vm/src/vm/mod.rs @@ -305,25 +305,29 @@ impl VirtualMachine { // builtins.open to io.OpenWrapper, but this is easier, since it doesn't // require the Python stdlib to be present let io = import::import_builtin(self, "_io")?; - let set_stdio = |name, fd, mode: &str| { - let stdio = crate::stdlib::io::open( - self.ctx.new_int(fd).into(), - Some(mode), - Default::default(), - self, - )?; - let dunder_name = self.ctx.intern_str(format!("__{name}__")); - self.sys_module.set_attr( - dunder_name, // e.g. __stdin__ - stdio.clone(), - self, - )?; - self.sys_module.set_attr(name, stdio, self)?; - Ok(()) - }; - set_stdio("stdin", 0, "r")?; - set_stdio("stdout", 1, "w")?; - set_stdio("stderr", 2, "w")?; + + #[cfg(feature = "stdio")] + { + let set_stdio = |name, fd, mode: &str| { + let stdio = crate::stdlib::io::open( + self.ctx.new_int(fd).into(), + Some(mode), + Default::default(), + self, + )?; + let dunder_name = self.ctx.intern_str(format!("__{name}__")); + self.sys_module.set_attr( + dunder_name, // e.g. __stdin__ + stdio.clone(), + self, + )?; + self.sys_module.set_attr(name, stdio, self)?; + Ok(()) + }; + set_stdio("stdin", 0, "r")?; + set_stdio("stdout", 1, "w")?; + set_stdio("stderr", 2, "w")?; + } let io_open = io.get_attr("open", self)?; self.builtins.set_attr("open", io_open, self)?; From d4da84c85262244c46b2dce836950bf153fe9265 Mon Sep 17 00:00:00 2001 From: Daniel Stuart Date: Sun, 13 Oct 2024 23:25:30 -0300 Subject: [PATCH 2/2] Set stdio to None if feature is disabled More in line with what CPython does --- vm/src/vm/mod.rs | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/vm/src/vm/mod.rs b/vm/src/vm/mod.rs index 5c3b386e524..dfe420ece7d 100644 --- a/vm/src/vm/mod.rs +++ b/vm/src/vm/mod.rs @@ -14,6 +14,8 @@ mod vm_new; mod vm_object; mod vm_ops; +#[cfg(not(feature = "stdio"))] +use crate::builtins::PyNone; use crate::{ builtins::{ code::PyCode, @@ -306,28 +308,30 @@ impl VirtualMachine { // require the Python stdlib to be present let io = import::import_builtin(self, "_io")?; - #[cfg(feature = "stdio")] - { - let set_stdio = |name, fd, mode: &str| { - let stdio = crate::stdlib::io::open( - self.ctx.new_int(fd).into(), - Some(mode), - Default::default(), - self, - )?; - let dunder_name = self.ctx.intern_str(format!("__{name}__")); - self.sys_module.set_attr( - dunder_name, // e.g. __stdin__ - stdio.clone(), - self, - )?; - self.sys_module.set_attr(name, stdio, self)?; - Ok(()) - }; - set_stdio("stdin", 0, "r")?; - set_stdio("stdout", 1, "w")?; - set_stdio("stderr", 2, "w")?; - } + let set_stdio = |name, _fd, _mode: &str| { + #[cfg(feature = "stdio")] + let stdio = crate::stdlib::io::open( + self.ctx.new_int(_fd).into(), + Some(_mode), + Default::default(), + self, + )?; + + #[cfg(not(feature = "stdio"))] + let stdio = PyNone.into_pyobject(self); + + let dunder_name = self.ctx.intern_str(format!("__{name}__")); + self.sys_module.set_attr( + dunder_name, // e.g. __stdin__ + stdio.clone(), + self, + )?; + self.sys_module.set_attr(name, stdio, self)?; + Ok(()) + }; + set_stdio("stdin", 0, "r")?; + set_stdio("stdout", 1, "w")?; + set_stdio("stderr", 2, "w")?; let io_open = io.get_attr("open", self)?; self.builtins.set_attr("open", io_open, self)?;