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}