1use proc_macro::{Span, TokenStream};
42use pyo3::{PyObject, PyResult, Python, prelude::*};
43use std::{ffi::CString, ptr::null_mut, str::FromStr};
44
45mod shared;
46use shared::*;
47
48#[proc_macro]
52pub fn ct_python(input: TokenStream) -> TokenStream {
53 ct_python_impl(input).unwrap_or_else(|e| e)
54}
55
56fn ct_python_impl(input: TokenStream) -> Result<TokenStream, TokenStream> {
57 let python = CString::new(python_from_macro(input.clone(), None)?).unwrap();
58 let filename = CString::new(Span::call_site().file()).unwrap();
59
60 Python::with_gil(|py| {
61 let code = compile_python(py, &python, &filename, input.clone())?;
62 let output = run_and_capture(py, code)
63 .map_err(|err| python_error_to_compile_error(py, err, input))?;
64 TokenStream::from_str(&output)
65 .map_err(|_| compile_error(None, "produced invalid Rust code"))
66 })
67}
68
69fn run_and_capture(py: Python, code: PyObject) -> PyResult<String> {
70 #[cfg(unix)]
71 let _ = ensure_libpython_symbols_loaded(py);
72
73 let globals = py.import("__main__")?.dict().copy()?;
74
75 let sys = py.import("sys")?;
76 let stdout = py.import("io")?.getattr("StringIO")?.call0()?;
77 let original_stdout = sys.dict().get_item("stdout")?;
78 sys.dict().set_item("stdout", &stdout)?;
79
80 let result = unsafe {
81 let ptr = pyo3::ffi::PyEval_EvalCode(code.as_ptr(), globals.as_ptr(), null_mut());
82 PyObject::from_owned_ptr_or_err(py, ptr)
83 };
84
85 sys.dict().set_item("stdout", original_stdout)?;
86
87 result?;
88
89 stdout.call_method0("getvalue")?.extract()
90}
91
92#[cfg(unix)]
93fn ensure_libpython_symbols_loaded(py: Python) -> PyResult<()> {
94 let sysconfig = py.import("sysconfig")?;
103 let libdir: String = sysconfig
104 .getattr("get_config_var")?
105 .call1(("LIBDIR",))?
106 .extract()?;
107 let so_name: String = sysconfig
108 .getattr("get_config_var")?
109 .call1(("INSTSONAME",))?
110 .extract()?;
111 let path = CString::new(format!("{libdir}/{so_name}")).unwrap();
112 unsafe { libc::dlopen(path.as_ptr(), libc::RTLD_NOW | libc::RTLD_GLOBAL) };
113 Ok(())
114}