//! utilities for calling CPython. //! //! CPythonを呼び出すためのユーティリティー use std::process::Command; use crate::serialize::get_magic_num_from_bytes; #[cfg(unix)] pub const BUILTIN_PYTHON_MODS: [&str; 176] = [ "abc", "argparse", "array", "ast", "asyncio", "atexit", "base64", "bdb", "binascii", "binhex", "bisect", "builtins", "bz2", "calendar", "cmath", "code", "codecs", "codeop", "collections", "colorsys", "compileall", "concurrent", "configparser", "contextlib", "contextvars", "copy", "copyreg", "cProfile", "csv", "ctypes", "curses", "dataclasses", "datetime", "dbm", "decimal", "difflib", "dis", "distutils", "doctest", "email", "encodings", "ensurepip", "enum", "errno", "faulthandler", "fcntl", "filecmp", "fileinput", "fnmatch", "fractions", "ftplib", "functools", "gc", "getopt", "getpass", "gettext", "glob", "graphlib", "grp", "gzip", "hashlib", "heapq", "hmac", "html", "http", "imaplib", "importlib", "inspect", "io", "ipaddress", "itertools", "json", "keyword", "lib2to3", "linecache", "locale", "logging", "lzma", "mailbox", "marshal", "math", "mimetypes", "mmap", "modulefinder", "multiprocessing", "netrc", "numbers", "operator", "os", "pathlib", "pdb", "pickle", "pickletools", "pkgutil", "platform", "plistlib", "poplib", "posix", "pprint", "profile", "pstats", "pty", "pwd", "py_compile", "pyclbr", "pydoc", "queue", "quopri", "random", "re", "readline", "reprlib", "resource", "rlcompleter", "runpy", "sched", "secrets", "select", "selectors", "shelve", "shlex", "shutil", "signal", "site", "smtplib", "socket", "socketserver", "sqlite3", "ssl", "stat", "statistics", "string", "stringprep", "struct", "subprocess", "symtable", "sys", "sysconfig", "syslog", "tabnanny", "tarfile", "tempfile", "termios", "test", "textwrap", "threading", "time", "timeit", "tkinter", "token", "tokenize", "trace", "traceback", "tracemalloc", "tty", "turtle", "turtledemo", "types", "typing", "unicodedata", "unittest", "urllib", "uuid", "venv", "warnings", "wave", "weakref", "webbrowser", "wsgiref", "xml", "xmlrpc", "zipapp", "zipfile", "zipimport", "zlib", "zoneinfo", ]; #[cfg(windows)] pub const BUILTIN_PYTHON_MODS: [&str; 170] = [ "argparse", "array", "ast", "asyncio", "atexit", "base64", "bdb", "binascii", "binhex", "bisect", "builtins", "bz2", "calendar", "cmath", "code", "codecs", "codeop", "collections", "colorsys", "compileall", "concurrent", "configparser", "contextlib", "contextvars", "copy", "copyreg", "cProfile", "csv", "ctypes", "curses", "dataclasses", "datetime", "dbm", "decimal", "difflib", "dis", "distutils", "doctest", "email", "encodings", "ensurepip", "enum", "errno", "faulthandler", "filecmp", "fileinput", "fnmatch", "fractions", "ftplib", "functools", "gc", "getopt", "getpass", "gettext", "glob", "graphlib", "gzip", "hashlib", "heapq", "hmac", "html", "http", "imaplib", "importlib", "inspect", "io", "ipaddress", "itertools", "json", "keyword", "lib2to3", "linecache", "locale", "logging", "lzma", "mailbox", "marshal", "math", "mimetypes", "mmap", "modulefinder", "msvcrt", "multiprocessing", "netrc", "numbers", "operator", "os", "pathlib", "pdb", "pickle", "pickletools", "pkgutil", "plistlib", "poplib", "platform", "plistlib", "poplib", "pprint", "profile", "pstats", "py_compile", "pyclbr", "pydoc", "queue", "quopri", "random", "re", "reprlib", "rlcompleter", "runpy", "sched", "secrets", "select", "selectors", "shelve", "shlex", "shutil", "signal", "site", "smtplib", "socket", "socketserver", "sqlite3", "ssl", "stat", "statistics", "string", "stringprep", "struct", "subprocess", "symtable", "sys", "sysconfig", "tabnanny", "tarfile", "tempfile", "test", "textwrap", "threading", "time", "timeit", "tkinter", "token", "tokenize", "trace", "traceback", "tracemalloc", "turtle", "turtledemo", "types", "typing", "unicodedata", "unittest", "urllib", "uuid", "venv", "warnings", "wave", "weakref", "webbrowser", "winreg", "winsound", "wsgiref", "xml", "xmlrpc", "zipapp", "zipfile", "zipimport", "zlib", "zoneinfo", ]; pub fn which_python() -> String { let (cmd, python) = if cfg!(windows) { ("where", "python") } else { ("which", "python3") }; let out = Command::new(cmd) .arg(python) .output() .expect("python not found"); let res = String::from_utf8(out.stdout).unwrap(); let res = res.split('\n').next().unwrap_or("").replace('\r', ""); if res.is_empty() { println!("python not found"); std::process::exit(1); } else if res.contains("pyenv") && cfg!(windows) { println!("cannot use pyenv-win"); // because pyenv-win does not support `-c` option std::process::exit(1); } res } pub fn detect_magic_number() -> u32 { let out = if cfg!(windows) { Command::new("cmd") .arg("/C") .arg(which_python()) .arg("-c") .arg("import importlib.util as util;print(util.MAGIC_NUMBER.hex())") .output() .expect("cannot get the magic number from python") } else { let python_command = format!( "{} -c 'import importlib.util as util;print(util.MAGIC_NUMBER.hex())'", which_python() ); Command::new("sh") .arg("-c") .arg(python_command) .output() .expect("cannot get the magic number from python") }; let s_hex_magic_num = String::from_utf8(out.stdout).unwrap(); let first_byte = u8::from_str_radix(&s_hex_magic_num[0..=1], 16).unwrap(); let second_byte = u8::from_str_radix(&s_hex_magic_num[2..=3], 16).unwrap(); get_magic_num_from_bytes(&[first_byte, second_byte, 0, 0]) } /// executes over a shell, cause `python` may not exist as an executable file (like pyenv) pub fn exec_pyc>(file: S) -> Option { let mut out = if cfg!(windows) { Command::new("cmd") .arg("/C") .arg(which_python()) .arg(&file.into()) .spawn() .expect("cannot execute python") } else { let python_command = format!("{} {}", which_python(), file.into()); Command::new("sh") .arg("-c") .arg(python_command) .spawn() .expect("cannot execute python") }; out.wait().expect("python doesn't work").code() } /// evaluates over a shell, cause `python` may not exist as an executable file (like pyenv) pub fn eval_pyc>(file: S) -> String { let out = if cfg!(windows) { Command::new("cmd") .arg("/C") .arg(which_python()) .arg(&file.into()) .spawn() .expect("cannot execute python") } else { let python_command = format!("{} {}", which_python(), file.into()); Command::new("sh") .arg("-c") .arg(python_command) .spawn() .expect("cannot execute python") }; let out = out.wait_with_output().expect("python doesn't work"); String::from_utf8_lossy(&out.stdout).to_string() } pub fn exec_py(code: &str) -> Option { let mut child = if cfg!(windows) { Command::new(which_python()) .arg("-c") .arg(code) .spawn() .expect("cannot execute python") } else { let python_command = format!("{} -c \"{}\"", which_python(), code); Command::new("sh") .arg("-c") .arg(python_command) .spawn() .expect("cannot execute python") }; child.wait().expect("python doesn't work").code() } pub fn spawn_py(code: &str) { if cfg!(windows) { Command::new(which_python()) .arg("-c") .arg(code) .spawn() .expect("cannot execute python"); } else { let python_command = format!("{} -c \"{}\"", which_python(), code); Command::new("sh") .arg("-c") .arg(python_command) .spawn() .expect("cannot execute python"); } }