blr/utils/python.rs
1use crate::{result::Result, BlError};
2use pyo3::{
3 types::{PyDict, PyModule},
4 Python,
5};
6use std::{fs::read_to_string, path::Path};
7
8/// Run Python code that uses the Python API of Blender (`bpy`).
9///
10/// Code from a Python script can either be included at compile time using the
11/// `std::include_str!` macro or at runtime using `std::fs::read_to_string` function.
12///
13/// # Examples
14///
15/// ```
16/// use blr::utils::python::run_bpy_code;
17/// use pyo3::Python;
18///
19/// Python::with_gil(|py| {
20/// run_bpy_code(py, "print(f'Blender version: {bpy.app.version}')").unwrap();
21/// });
22/// ```
23///
24/// ```no_compile
25/// use blr::utils::python::run_bpy_code;
26/// use pyo3::Python;
27///
28/// let code = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/python/test.py"));
29///
30/// Python::with_gil(|py| {
31/// run_bpy_code(py, code).unwrap();
32/// });
33/// ```
34pub fn run_bpy_code(py: Python, code: &str) -> Result<()> {
35 // Ensure that `bpy` module is in globals
36 let globals = PyDict::new(py);
37 globals.set_item("bpy", PyModule::import(py, "bpy")?)?;
38
39 // Run the code
40 py.run(code, Some(globals), None)?;
41
42 Ok(())
43}
44
45/// Run a Python script that uses the Python API of Blender (`bpy`).
46///
47/// The script is read from the given file path at runtime.
48///
49/// # Examples
50///
51/// ```no_run
52/// use blr::utils::python::run_bpy_script;
53/// use pyo3::Python;
54///
55/// Python::with_gil(|py| {
56/// run_bpy_script(py, &"test.py").unwrap();
57/// });
58/// ```
59pub fn run_bpy_script(py: Python, filepath: impl AsRef<Path>) -> Result<()> {
60 let filepath = filepath.as_ref();
61 if !filepath.is_file() {
62 return Err(BlError::ValueError(format!(
63 "Filepath '{}' does not point to a valid file.",
64 filepath.display()
65 )));
66 }
67
68 run_bpy_code(py, &read_to_string(filepath)?)
69}