stable_inline_python/
lib.rs

1use std::{str::FromStr, fs};
2
3use pyo3::{prelude::*, types::PyDict};
4
5#[macro_export]
6macro_rules! py_eval {
7    ($val:expr) => {
8        {
9            use pyo3::prelude::*;
10            pyo3::prepare_freethreaded_python();
11
12            Python::with_gil(|py| {
13                py.run($val, None, None)
14            })
15        }
16    };
17}
18
19/// Represents a context for executing Python code.
20pub struct PyContext {
21    /// Stores Python variables and their values.
22    pub variables: PyVar
23}
24
25impl Default for PyContext {
26    /// Constructs a new `PyContext` with default values.
27    fn default() -> Self {
28        PyContext { variables: Default::default() }
29    }
30}
31
32/// Represents Python variables and their values.
33pub struct PyVar {
34    /// Stores local Python variables.
35    pub locals: Py<PyDict>
36}
37
38impl Default for PyVar {
39    /// Constructs a new `PyVar` with default values.
40    fn default() -> Self {
41        pyo3::prepare_freethreaded_python();
42        Python::with_gil(|py| {
43            Self { locals: PyDict::new(py).into() }
44        })
45    }
46}
47
48impl PyContext {
49    /// Constructs a new empty `PyContext`.
50    pub fn new() -> PyContext {
51        PyContext { ..Default::default() }
52        
53    }
54
55    /// Executes Python code provided as input.
56    ///
57    /// # Arguments
58    ///
59    /// * `input` - A string containing the Python code to execute.
60    pub fn run(&self, input: &str) -> Result<(), pyo3::PyErr> {
61        self.execute_python(Some(&self.variables), &input)
62    }
63
64    /// Executes Python code from a file specified by `file`.
65    ///
66    /// # Arguments
67    ///
68    /// * `file` - The path to the file containing Python code.
69    ///
70    /// # Errors
71    ///
72    /// Returns an error if there is an issue reading the file or executing Python code.
73    pub fn run_file(&self, file: &str) -> Result<(), PyErr> {
74        let contents = fs::read_to_string(&file)?;
75        self.execute_python(Some(&self.variables), &contents)
76    }
77     
78    
79    /// Retrieves the value of a Python variable identified by `input`.
80    ///
81    /// # Arguments
82    ///
83    /// * `input` - The name of the Python variable to retrieve.
84    ///
85    /// # Errors
86    ///
87    /// Returns an error if parsing of the variable value fails.
88    pub fn get<T: FromStr>(&self, input: &str) -> Result<T, <T as FromStr>::Err> {
89
90        pyo3::prepare_freethreaded_python();
91
92        let out = Python::with_gil(|py| {
93            let locals: &PyDict = self._define(Some(&self.variables), &py);
94            
95            let ret = locals.get_item(&input).unwrap().unwrap();
96            format!("{}",ret)
97        });
98        out.parse::<T>()
99    }
100
101    fn execute_python(&self, py_vars: Option<&PyVar>, input: &str) -> Result<(), pyo3::PyErr> {
102        pyo3::prepare_freethreaded_python();
103    
104        Python::with_gil(|py| {
105            let locals: &PyDict = self._define(py_vars, &py);
106            py.run(&input, None, Some(locals))
107        })
108    }
109
110    /// Defines Python variables for execution.
111    ///
112    /// # Arguments
113    ///
114    /// * `py_vars` - Optional Python variables and their values.
115    /// * `py` - A Python interpreter.
116    ///
117    /// # Returns
118    ///
119    /// A reference to a Python dictionary containing the defined variables.
120    fn _define<'a>(&self, py_vars: Option<&'a PyVar>, py: &Python<'a>) -> &'a PyDict {
121        if py_vars.is_none() {
122            PyDict::new(*py).into()
123        } else {
124            py_vars.unwrap().locals.as_ref(*py)
125        }
126    }
127    
128}