From ee8bcf492189fc8999e8f013e84b7886bb6fc566 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Sun, 14 Jul 2019 09:59:27 +0200 Subject: [PATCH 1/3] Move processing of PYTHONPATH out of vm. --- src/main.rs | 127 ++++++++++++++++++++++++++++++++++++-------- vm/src/sysmodule.rs | 31 +++-------- vm/src/vm.rs | 4 ++ 3 files changed, 116 insertions(+), 46 deletions(-) diff --git a/src/main.rs b/src/main.rs index deec991d6..7762b56aa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,14 +17,36 @@ use rustpython_vm::{ }; use std::convert::TryInto; +use std::env; use std::path::PathBuf; use std::process; +use std::str::FromStr; fn main() { #[cfg(feature = "flame-it")] let main_guard = flame::start_guard("RustPython main"); env_logger::init(); - let app = App::new("RustPython") + let app = App::new("RustPython"); + let matches = parse_arguments(app); + let settings = create_settings(&matches); + let vm = VirtualMachine::new(settings); + + let res = run_rustpython(&vm, matches); + // See if any exception leaked out: + handle_exception(&vm, res); + + #[cfg(feature = "flame-it")] + { + main_guard.end(); + if let Err(e) = write_profile(matches) { + error!("Error writing profile information: {}", e); + process::exit(1); + } + } +} + +fn parse_arguments<'a>(app: App<'a, '_>) -> ArgMatches<'a> { + let app = app .version(crate_version!()) .author(crate_authors!()) .about("Rust implementation of the Python language") @@ -62,6 +84,11 @@ fn main() { .short("S") .help("don't imply 'import site' on initialization"), ) + .arg( + Arg::with_name("ignore-environment") + .short("E") + .help("Ignore environment variables PYTHON* such as PYTHONPATH"), + ) .arg( Arg::with_name("c") .short("c") @@ -90,33 +117,89 @@ fn main() { .help("the profile format to output the profiling information in"), ); let matches = app.get_matches(); + matches +} - let opt_level: u8 = matches.occurrences_of("optimize").try_into().unwrap(); - let verbosity_level: u8 = matches.occurrences_of("verbose").try_into().unwrap(); - - // Construct vm: +/// Create settings by examining command line arguments and environment +/// variables. +fn create_settings(matches: &ArgMatches) -> PySettings { + let ignore_environment = matches.is_present("ignore-environment"); let mut settings: PySettings = Default::default(); - settings.debug = matches.is_present("debug"); - settings.inspect = matches.is_present("inspect"); - settings.optimize = opt_level; - settings.no_site = matches.is_present("no-site"); - settings.no_user_site = matches.is_present("no-user-site"); - settings.verbose = verbosity_level; - settings.quiet = matches.is_present("quiet"); - let vm = VirtualMachine::new(settings); + settings.ignore_environment = ignore_environment; - let res = run_rustpython(&vm, matches); - // See if any exception leaked out: - handle_exception(&vm, res); + if !ignore_environment { + settings.path_list.append(&mut get_paths("RUSTPYTHONPATH")); + settings.path_list.append(&mut get_paths("PYTHONPATH")); + } - #[cfg(feature = "flame-it")] - { - main_guard.end(); - if let Err(e) = write_profile(matches) { - error!("Error writing profile information: {}", e); - process::exit(1); + // Now process command line flags: + if matches.is_present("debug") { + settings.debug = true; + } else if !ignore_environment && env::var_os("PYTHONDEBUG").is_some() { + settings.debug = true; + } + + if matches.is_present("inspect") { + settings.inspect = true; + } else if !ignore_environment && env::var_os("PYTHONINSPECT").is_some() { + settings.inspect = true; + } + + if matches.is_present("optimize") { + settings.optimize = matches.occurrences_of("optimize").try_into().unwrap(); + } else if !ignore_environment { + if let Some(value) = get_env_var_value("PYTHONOPTIMIZE") { + settings.optimize = value; } } + + if matches.is_present("verbose") { + settings.verbose = matches.occurrences_of("verbose").try_into().unwrap(); + } else if !ignore_environment { + if let Some(value) = get_env_var_value("PYTHONVERBOSE") { + settings.verbose = value; + } + } + + settings.no_site = matches.is_present("no-site"); + + if matches.is_present("no-user-site") { + settings.no_user_site = true; + } else if !ignore_environment && env::var_os("PYTHONNOUSERSITE").is_some() { + settings.no_user_site = true; + } + + if matches.is_present("quiet") { + settings.quiet = true; + } + + settings +} + +/// Get environment variable and turn it into integer. +fn get_env_var_value(name: &str) -> Option { + env::var_os(name).map(|value| { + if let Ok(value) = u8::from_str(&value.into_string().unwrap()) { + value + } else { + 1 + } + }) +} + +/// Helper function to retrieve a sequence of paths from an environment variable. +fn get_paths(env_variable_name: &str) -> Vec { + let paths = env::var_os(env_variable_name); + match paths { + Some(paths) => env::split_paths(&paths) + .map(|path| { + path.into_os_string() + .into_string() + .unwrap_or_else(|_| panic!("{} isn't valid unicode", env_variable_name)) + }) + .collect(), + None => vec![], + } } #[cfg(feature = "flame-it")] diff --git a/vm/src/sysmodule.rs b/vm/src/sysmodule.rs index 10aeb4747..00c7017ce 100644 --- a/vm/src/sysmodule.rs +++ b/vm/src/sysmodule.rs @@ -129,30 +129,13 @@ pub fn make_module(vm: &VirtualMachine, module: PyObjectRef, builtins: PyObjectR "cache_tag" => ctx.new_str("rustpython-01".to_string()), }); - let path_list = if cfg!(target_arch = "wasm32") { - vec![] - } else { - fn get_paths(env_variable_name: &str) -> Vec { - let paths = env::var_os(env_variable_name); - match paths { - Some(paths) => env::split_paths(&paths) - .map(|path| { - path.into_os_string() - .into_string() - .unwrap_or_else(|_| panic!("{} isn't valid unicode", env_variable_name)) - }) - .collect(), - None => vec![], - } - } - - get_paths("RUSTPYTHONPATH") - .into_iter() - .chain(get_paths("PYTHONPATH").into_iter()) - .map(|path| ctx.new_str(path)) - .collect() - }; - let path = ctx.new_list(path_list); + let path = ctx.new_list( + vm.settings + .path_list + .iter() + .map(|path| ctx.new_str(path.clone())) + .collect(), + ); let platform = if cfg!(target_os = "linux") { "linux".to_string() diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 232eca0e1..74e42ef6d 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -84,6 +84,9 @@ pub struct PySettings { /// -q pub quiet: bool, + + /// Environment PYTHONPATH and RUSTPYTHONPATH: + pub path_list: Vec, } /// Sensible default settings. @@ -98,6 +101,7 @@ impl Default for PySettings { ignore_environment: false, verbose: 0, quiet: false, + path_list: vec![], } } } From 49afefd4f24718cb25a9782142f802e65c1f11f8 Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Sun, 14 Jul 2019 10:44:33 +0200 Subject: [PATCH 2/3] Implement ideas from clippy. --- src/main.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/main.rs b/src/main.rs index 7762b56aa..5e10e038f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -116,8 +116,7 @@ fn parse_arguments<'a>(app: App<'a, '_>) -> ArgMatches<'a> { .takes_value(true) .help("the profile format to output the profiling information in"), ); - let matches = app.get_matches(); - matches + app.get_matches() } /// Create settings by examining command line arguments and environment @@ -133,15 +132,14 @@ fn create_settings(matches: &ArgMatches) -> PySettings { } // Now process command line flags: - if matches.is_present("debug") { - settings.debug = true; - } else if !ignore_environment && env::var_os("PYTHONDEBUG").is_some() { + if matches.is_present("debug") || (!ignore_environment && env::var_os("PYTHONDEBUG").is_some()) + { settings.debug = true; } - if matches.is_present("inspect") { - settings.inspect = true; - } else if !ignore_environment && env::var_os("PYTHONINSPECT").is_some() { + if matches.is_present("inspect") + || (!ignore_environment && env::var_os("PYTHONINSPECT").is_some()) + { settings.inspect = true; } @@ -163,9 +161,9 @@ fn create_settings(matches: &ArgMatches) -> PySettings { settings.no_site = matches.is_present("no-site"); - if matches.is_present("no-user-site") { - settings.no_user_site = true; - } else if !ignore_environment && env::var_os("PYTHONNOUSERSITE").is_some() { + if matches.is_present("no-user-site") + || (!ignore_environment && env::var_os("PYTHONNOUSERSITE").is_some()) + { settings.no_user_site = true; } From cdfcf5278c0ce36d4f2946c65471c39546bffe8f Mon Sep 17 00:00:00 2001 From: Windel Bouwman Date: Sun, 14 Jul 2019 21:39:26 +0200 Subject: [PATCH 3/3] Use env::var where applicable. Fix flame-it feature build. --- src/main.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main.rs b/src/main.rs index 5e10e038f..1da922848 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,14 +31,14 @@ fn main() { let settings = create_settings(&matches); let vm = VirtualMachine::new(settings); - let res = run_rustpython(&vm, matches); + let res = run_rustpython(&vm, &matches); // See if any exception leaked out: handle_exception(&vm, res); #[cfg(feature = "flame-it")] { main_guard.end(); - if let Err(e) = write_profile(matches) { + if let Err(e) = write_profile(&matches) { error!("Error writing profile information: {}", e); process::exit(1); } @@ -146,7 +146,7 @@ fn create_settings(matches: &ArgMatches) -> PySettings { if matches.is_present("optimize") { settings.optimize = matches.occurrences_of("optimize").try_into().unwrap(); } else if !ignore_environment { - if let Some(value) = get_env_var_value("PYTHONOPTIMIZE") { + if let Ok(value) = get_env_var_value("PYTHONOPTIMIZE") { settings.optimize = value; } } @@ -154,7 +154,7 @@ fn create_settings(matches: &ArgMatches) -> PySettings { if matches.is_present("verbose") { settings.verbose = matches.occurrences_of("verbose").try_into().unwrap(); } else if !ignore_environment { - if let Some(value) = get_env_var_value("PYTHONVERBOSE") { + if let Ok(value) = get_env_var_value("PYTHONVERBOSE") { settings.verbose = value; } } @@ -175,9 +175,9 @@ fn create_settings(matches: &ArgMatches) -> PySettings { } /// Get environment variable and turn it into integer. -fn get_env_var_value(name: &str) -> Option { - env::var_os(name).map(|value| { - if let Ok(value) = u8::from_str(&value.into_string().unwrap()) { +fn get_env_var_value(name: &str) -> Result { + env::var(name).map(|value| { + if let Ok(value) = u8::from_str(&value) { value } else { 1 @@ -201,7 +201,7 @@ fn get_paths(env_variable_name: &str) -> Vec { } #[cfg(feature = "flame-it")] -fn write_profile(matches: ArgMatches) -> Result<(), Box> { +fn write_profile(matches: &ArgMatches) -> Result<(), Box> { use std::fs::File; enum ProfileFormat { @@ -244,7 +244,7 @@ fn write_profile(matches: ArgMatches) -> Result<(), Box> Ok(()) } -fn run_rustpython(vm: &VirtualMachine, matches: ArgMatches) -> PyResult<()> { +fn run_rustpython(vm: &VirtualMachine, matches: &ArgMatches) -> PyResult<()> { import::init_importlib(&vm, true)?; if let Some(paths) = option_env!("BUILDTIME_RUSTPYTHONPATH") {