rustpython_vm/vm/
compile.rs

1use crate::{
2    builtins::{PyCode, PyDictRef},
3    compiler::{self, CompileError, CompileOpts},
4    convert::TryFromObject,
5    scope::Scope,
6    AsObject, PyObjectRef, PyRef, PyResult, VirtualMachine,
7};
8
9impl VirtualMachine {
10    pub fn compile(
11        &self,
12        source: &str,
13        mode: compiler::Mode,
14        source_path: String,
15    ) -> Result<PyRef<PyCode>, CompileError> {
16        self.compile_with_opts(source, mode, source_path, self.compile_opts())
17    }
18
19    pub fn compile_with_opts(
20        &self,
21        source: &str,
22        mode: compiler::Mode,
23        source_path: String,
24        opts: CompileOpts,
25    ) -> Result<PyRef<PyCode>, CompileError> {
26        compiler::compile(source, mode, source_path, opts).map(|code| self.ctx.new_code(code))
27    }
28
29    pub fn run_script(&self, scope: Scope, path: &str) -> PyResult<()> {
30        if get_importer(path, self)?.is_some() {
31            self.insert_sys_path(self.new_pyobj(path))?;
32            let runpy = self.import("runpy", 0)?;
33            let run_module_as_main = runpy.get_attr("_run_module_as_main", self)?;
34            run_module_as_main.call((identifier!(self, __main__).to_owned(), false), self)?;
35            return Ok(());
36        }
37
38        let dir = std::path::Path::new(path)
39            .parent()
40            .unwrap()
41            .to_str()
42            .unwrap();
43        self.insert_sys_path(self.new_pyobj(dir))?;
44
45        match std::fs::read_to_string(path) {
46            Ok(source) => {
47                self.run_code_string(scope, &source, path.to_owned())?;
48            }
49            Err(err) => {
50                error!("Failed reading file '{}': {}", path, err);
51                // TODO: Need to change to ExitCode or Termination
52                std::process::exit(1);
53            }
54        }
55        Ok(())
56    }
57
58    pub fn run_code_string(&self, scope: Scope, source: &str, source_path: String) -> PyResult {
59        let code_obj = self
60            .compile(source, compiler::Mode::Exec, source_path.clone())
61            .map_err(|err| self.new_syntax_error(&err, Some(source)))?;
62        // trace!("Code object: {:?}", code_obj.borrow());
63        scope.globals.set_item(
64            identifier!(self, __file__),
65            self.new_pyobj(source_path),
66            self,
67        )?;
68        self.run_code_obj(code_obj, scope)
69    }
70
71    pub fn run_block_expr(&self, scope: Scope, source: &str) -> PyResult {
72        let code_obj = self
73            .compile(source, compiler::Mode::BlockExpr, "<embedded>".to_owned())
74            .map_err(|err| self.new_syntax_error(&err, Some(source)))?;
75        // trace!("Code object: {:?}", code_obj.borrow());
76        self.run_code_obj(code_obj, scope)
77    }
78}
79
80fn get_importer(path: &str, vm: &VirtualMachine) -> PyResult<Option<PyObjectRef>> {
81    let path_importer_cache = vm.sys_module.get_attr("path_importer_cache", vm)?;
82    let path_importer_cache = PyDictRef::try_from_object(vm, path_importer_cache)?;
83    if let Some(importer) = path_importer_cache.get_item_opt(path, vm)? {
84        return Ok(Some(importer));
85    }
86    let path = vm.ctx.new_str(path);
87    let path_hooks = vm.sys_module.get_attr("path_hooks", vm)?;
88    let mut importer = None;
89    let path_hooks: Vec<PyObjectRef> = path_hooks.try_into_value(vm)?;
90    for path_hook in path_hooks {
91        match path_hook.call((path.clone(),), vm) {
92            Ok(imp) => {
93                importer = Some(imp);
94                break;
95            }
96            Err(e) if e.fast_isinstance(vm.ctx.exceptions.import_error) => continue,
97            Err(e) => return Err(e),
98        }
99    }
100    Ok(if let Some(imp) = importer {
101        let imp = path_importer_cache.get_or_insert(vm, path.into(), || imp.clone())?;
102        Some(imp)
103    } else {
104        None
105    })
106}