Thanks to visit codestin.com
Credit goes to docs.rs

rustpython/
settings.rs

1use clap::{App, AppSettings, Arg, ArgMatches};
2use rustpython_vm::Settings;
3use std::{env, str::FromStr};
4
5pub enum RunMode {
6    ScriptInteractive(Option<String>, bool),
7    Command(String),
8    Module(String),
9    InstallPip(String),
10}
11
12pub fn opts_with_clap() -> (Settings, RunMode) {
13    let app = App::new("RustPython");
14    let matches = parse_arguments(app);
15    settings_from(&matches)
16}
17
18fn parse_arguments<'a>(app: App<'a, '_>) -> ArgMatches<'a> {
19    let app = app
20        .setting(AppSettings::TrailingVarArg)
21        .version(crate_version!())
22        .author(crate_authors!())
23        .about("Rust implementation of the Python language")
24        .usage("rustpython [OPTIONS] [-c CMD | -m MODULE | FILE] [PYARGS]...")
25        .arg(
26            Arg::with_name("script")
27                .required(false)
28                .allow_hyphen_values(true)
29                .multiple(true)
30                .value_name("script, args")
31                .min_values(1),
32        )
33        .arg(
34            Arg::with_name("c")
35                .short("c")
36                .takes_value(true)
37                .allow_hyphen_values(true)
38                .multiple(true)
39                .value_name("cmd, args")
40                .min_values(1)
41                .help("run the given string as a program"),
42        )
43        .arg(
44            Arg::with_name("m")
45                .short("m")
46                .takes_value(true)
47                .allow_hyphen_values(true)
48                .multiple(true)
49                .value_name("module, args")
50                .min_values(1)
51                .help("run library module as script"),
52        )
53        .arg(
54            Arg::with_name("install_pip")
55                .long("install-pip")
56                .takes_value(true)
57                .allow_hyphen_values(true)
58                .multiple(true)
59                .value_name("get-pip args")
60                .min_values(0)
61                .help("install the pip package manager for rustpython; \
62                        requires rustpython be build with the ssl feature enabled."
63                ),
64        )
65        .arg(
66            Arg::with_name("optimize")
67                .short("O")
68                .multiple(true)
69                .help("Optimize. Set __debug__ to false. Remove debug statements."),
70        )
71        .arg(
72            Arg::with_name("verbose")
73                .short("v")
74                .multiple(true)
75                .help("Give the verbosity (can be applied multiple times)"),
76        )
77        .arg(Arg::with_name("debug").short("d").help("Debug the parser."))
78        .arg(
79            Arg::with_name("quiet")
80                .short("q")
81                .help("Be quiet at startup."),
82        )
83        .arg(
84            Arg::with_name("inspect")
85                .short("i")
86                .help("Inspect interactively after running the script."),
87        )
88        .arg(
89            Arg::with_name("no-user-site")
90                .short("s")
91                .help("don't add user site directory to sys.path."),
92        )
93        .arg(
94            Arg::with_name("no-site")
95                .short("S")
96                .help("don't imply 'import site' on initialization"),
97        )
98        .arg(
99            Arg::with_name("dont-write-bytecode")
100                .short("B")
101                .help("don't write .pyc files on import"),
102        )
103        .arg(
104            Arg::with_name("safe-path")
105                .short("P")
106                .help("don’t prepend a potentially unsafe path to sys.path"),
107        )
108        .arg(
109            Arg::with_name("ignore-environment")
110                .short("E")
111                .help("Ignore environment variables PYTHON* such as PYTHONPATH"),
112        )
113        .arg(
114            Arg::with_name("isolate")
115                .short("I")
116                .help("isolate Python from the user's environment (implies -E and -s)"),
117        )
118        .arg(
119            Arg::with_name("implementation-option")
120                .short("X")
121                .takes_value(true)
122                .multiple(true)
123                .number_of_values(1)
124                .help("set implementation-specific option"),
125        )
126        .arg(
127            Arg::with_name("warning-control")
128                .short("W")
129                .takes_value(true)
130                .multiple(true)
131                .number_of_values(1)
132                .help("warning control; arg is action:message:category:module:lineno"),
133        )
134        .arg(
135            Arg::with_name("check-hash-based-pycs")
136                .long("check-hash-based-pycs")
137                .takes_value(true)
138                .number_of_values(1)
139                .default_value("default")
140                .help("always|default|never\ncontrol how Python invalidates hash-based .pyc files"),
141        )
142        .arg(
143            Arg::with_name("bytes-warning")
144                .short("b")
145                .multiple(true)
146                .help("issue warnings about using bytes where strings are usually expected (-bb: issue errors)"),
147        ).arg(
148            Arg::with_name("unbuffered")
149                .short("u")
150                .help(
151                    "force the stdout and stderr streams to be unbuffered; \
152                        this option has no effect on stdin; also PYTHONUNBUFFERED=x",
153                ),
154        );
155    #[cfg(feature = "flame-it")]
156    let app = app
157        .arg(
158            Arg::with_name("profile_output")
159                .long("profile-output")
160                .takes_value(true)
161                .help("the file to output the profiling information to"),
162        )
163        .arg(
164            Arg::with_name("profile_format")
165                .long("profile-format")
166                .takes_value(true)
167                .help("the profile format to output the profiling information in"),
168        );
169    app.get_matches()
170}
171
172/// Create settings by examining command line arguments and environment
173/// variables.
174fn settings_from(matches: &ArgMatches) -> (Settings, RunMode) {
175    let mut settings = Settings::default();
176    settings.isolated = matches.is_present("isolate");
177    settings.ignore_environment = matches.is_present("ignore-environment");
178    settings.interactive = !matches.is_present("c")
179        && !matches.is_present("m")
180        && (!matches.is_present("script") || matches.is_present("inspect"));
181    settings.bytes_warning = matches.occurrences_of("bytes-warning");
182    settings.import_site = !matches.is_present("no-site");
183
184    let ignore_environment = settings.ignore_environment || settings.isolated;
185
186    if !ignore_environment {
187        settings.path_list.extend(get_paths("RUSTPYTHONPATH"));
188        settings.path_list.extend(get_paths("PYTHONPATH"));
189    }
190
191    // Now process command line flags:
192    if matches.is_present("debug") || (!ignore_environment && env::var_os("PYTHONDEBUG").is_some())
193    {
194        settings.debug = true;
195    }
196
197    if matches.is_present("inspect")
198        || (!ignore_environment && env::var_os("PYTHONINSPECT").is_some())
199    {
200        settings.inspect = true;
201    }
202
203    if matches.is_present("optimize") {
204        settings.optimize = matches.occurrences_of("optimize").try_into().unwrap();
205    } else if !ignore_environment {
206        if let Ok(value) = get_env_var_value("PYTHONOPTIMIZE") {
207            settings.optimize = value;
208        }
209    }
210
211    if matches.is_present("verbose") {
212        settings.verbose = matches.occurrences_of("verbose").try_into().unwrap();
213    } else if !ignore_environment {
214        if let Ok(value) = get_env_var_value("PYTHONVERBOSE") {
215            settings.verbose = value;
216        }
217    }
218
219    if matches.is_present("no-user-site")
220        || matches.is_present("isolate")
221        || (!ignore_environment && env::var_os("PYTHONNOUSERSITE").is_some())
222    {
223        settings.user_site_directory = false;
224    }
225
226    if matches.is_present("quiet") {
227        settings.quiet = true;
228    }
229
230    if matches.is_present("dont-write-bytecode")
231        || (!ignore_environment && env::var_os("PYTHONDONTWRITEBYTECODE").is_some())
232    {
233        settings.write_bytecode = false;
234    }
235    if !ignore_environment && env::var_os("PYTHONINTMAXSTRDIGITS").is_some() {
236        settings.int_max_str_digits = match env::var("PYTHONINTMAXSTRDIGITS").unwrap().parse() {
237            Ok(digits) if digits == 0 || digits >= 640 => digits,
238            _ => {
239                error!("Fatal Python error: config_init_int_max_str_digits: PYTHONINTMAXSTRDIGITS: invalid limit; must be >= 640 or 0 for unlimited.\nPython runtime state: preinitialized");
240                std::process::exit(1);
241            }
242        };
243    }
244
245    if matches.is_present("safe-path")
246        || (!ignore_environment && env::var_os("PYTHONSAFEPATH").is_some())
247    {
248        settings.safe_path = true;
249    }
250
251    matches
252        .value_of("check-hash-based-pycs")
253        .unwrap_or("default")
254        .clone_into(&mut settings.check_hash_pycs_mode);
255
256    let mut dev_mode = false;
257    let mut warn_default_encoding = false;
258    if let Some(xopts) = matches.values_of("implementation-option") {
259        settings.xoptions.extend(xopts.map(|s| {
260            let mut parts = s.splitn(2, '=');
261            let name = parts.next().unwrap().to_owned();
262            let value = parts.next().map(ToOwned::to_owned);
263            if name == "dev" {
264                dev_mode = true
265            }
266            if name == "warn_default_encoding" {
267                warn_default_encoding = true
268            }
269            if name == "no_sig_int" {
270                settings.install_signal_handlers = false;
271            }
272            if name == "int_max_str_digits" {
273                settings.int_max_str_digits = match value.as_ref().unwrap().parse() {
274                    Ok(digits) if digits == 0 || digits >= 640 => digits,
275                    _ => {
276
277                    error!("Fatal Python error: config_init_int_max_str_digits: -X int_max_str_digits: invalid limit; must be >= 640 or 0 for unlimited.\nPython runtime state: preinitialized");
278                    std::process::exit(1);
279                    },
280                };
281            }
282            (name, value)
283        }));
284    }
285    settings.dev_mode = dev_mode;
286    if warn_default_encoding
287        || (!ignore_environment && env::var_os("PYTHONWARNDEFAULTENCODING").is_some())
288    {
289        settings.warn_default_encoding = true;
290    }
291
292    if dev_mode {
293        settings.warnoptions.push("default".to_owned())
294    }
295    if settings.bytes_warning > 0 {
296        let warn = if settings.bytes_warning > 1 {
297            "error::BytesWarning"
298        } else {
299            "default::BytesWarning"
300        };
301        settings.warnoptions.push(warn.to_owned());
302    }
303    if let Some(warnings) = matches.values_of("warning-control") {
304        settings.warnoptions.extend(warnings.map(ToOwned::to_owned));
305    }
306
307    let (mode, argv) = if let Some(mut cmd) = matches.values_of("c") {
308        let command = cmd.next().expect("clap ensure this exists");
309        let argv = std::iter::once("-c".to_owned())
310            .chain(cmd.map(ToOwned::to_owned))
311            .collect();
312        (RunMode::Command(command.to_owned()), argv)
313    } else if let Some(mut cmd) = matches.values_of("m") {
314        let module = cmd.next().expect("clap ensure this exists");
315        let argv = std::iter::once("PLACEHOLDER".to_owned())
316            .chain(cmd.map(ToOwned::to_owned))
317            .collect();
318        (RunMode::Module(module.to_owned()), argv)
319    } else if let Some(get_pip_args) = matches.values_of("install_pip") {
320        settings.isolated = true;
321        let mut args: Vec<_> = get_pip_args.map(ToOwned::to_owned).collect();
322        if args.is_empty() {
323            args.push("ensurepip".to_owned());
324            args.push("--upgrade".to_owned());
325            args.push("--default-pip".to_owned());
326        }
327        let installer = args[0].clone();
328        let mode = match installer.as_str() {
329            "ensurepip" | "get-pip" => RunMode::InstallPip(installer),
330            _ => panic!("--install-pip takes ensurepip or get-pip as first argument"),
331        };
332        (mode, args)
333    } else if let Some(argv) = matches.values_of("script") {
334        let argv: Vec<_> = argv.map(ToOwned::to_owned).collect();
335        let script = argv[0].clone();
336        (
337            RunMode::ScriptInteractive(Some(script), matches.is_present("inspect")),
338            argv,
339        )
340    } else {
341        (RunMode::ScriptInteractive(None, true), vec!["".to_owned()])
342    };
343
344    let hash_seed = match env::var("PYTHONHASHSEED") {
345        Ok(s) if s == "random" => Some(None),
346        Ok(s) => s.parse::<u32>().ok().map(Some),
347        Err(_) => Some(None),
348    };
349    settings.hash_seed = hash_seed.unwrap_or_else(|| {
350        error!("Fatal Python init error: PYTHONHASHSEED must be \"random\" or an integer in range [0; 4294967295]");
351        // TODO: Need to change to ExitCode or Termination
352        std::process::exit(1)
353    });
354
355    settings.argv = argv;
356
357    (settings, mode)
358}
359
360/// Get environment variable and turn it into integer.
361fn get_env_var_value(name: &str) -> Result<u8, std::env::VarError> {
362    env::var(name).map(|value| u8::from_str(&value).unwrap_or(1))
363}
364
365/// Helper function to retrieve a sequence of paths from an environment variable.
366fn get_paths(env_variable_name: &str) -> impl Iterator<Item = String> + '_ {
367    env::var_os(env_variable_name)
368        .into_iter()
369        .flat_map(move |paths| {
370            split_paths(&paths)
371                .map(|path| {
372                    path.into_os_string()
373                        .into_string()
374                        .unwrap_or_else(|_| panic!("{env_variable_name} isn't valid unicode"))
375                })
376                .collect::<Vec<_>>()
377        })
378}
379
380#[cfg(not(target_os = "wasi"))]
381pub(crate) use env::split_paths;
382#[cfg(target_os = "wasi")]
383pub(crate) fn split_paths<T: AsRef<std::ffi::OsStr> + ?Sized>(
384    s: &T,
385) -> impl Iterator<Item = std::path::PathBuf> + '_ {
386    use std::os::wasi::ffi::OsStrExt;
387    let s = s.as_ref().as_bytes();
388    s.split(|b| *b == b':')
389        .map(|x| std::ffi::OsStr::from_bytes(x).to_owned().into())
390}