inline_python_macros/
lib.rs1#![feature(proc_macro_span)]
4
5extern crate proc_macro;
6
7use proc_macro::{Delimiter, Group, Literal, Span, TokenStream, TokenTree};
8use pyo3::{Py, Python};
9use std::{
10 collections::BTreeMap,
11 ffi::{CStr, CString},
12};
13
14mod shared;
15use shared::*;
16
17#[doc(hidden)]
18#[proc_macro]
19pub fn python(input: TokenStream) -> TokenStream {
20 python_impl(input).unwrap_or_else(|e| e)
21}
22
23#[rustfmt::skip]
24fn python_impl(input: TokenStream) -> Result<TokenStream, TokenStream> {
25 let mut variables = BTreeMap::new();
26 let python = CString::new(python_from_macro(input.clone(), Some(&mut variables))?).unwrap();
27 let filename = CString::new(Span::call_site().file()).unwrap();
28 let bytecode = compile_to_bytecode(&python, &filename, input)?;
29 Ok(TokenStream::from_iter([
30 punct(':'), punct(':'), ident("inline_python"),
31 punct(':'), punct(':'), ident("FromInlinePython"),
32 punct(':'), punct(':'), ident("from_python_macro"),
33 parens([
34 TokenTree::Literal(bytecode), punct(','),
35 punct('|'), ident("globals"), punct('|'),
36 braces(variables.into_iter().flat_map(|(key, value)| [
37 punct(':'), punct(':'), ident("inline_python"),
38 punct(':'), punct(':'), ident("pyo3"),
39 punct(':'), punct(':'), ident("prelude"),
40 punct(':'), punct(':'), ident("PyDictMethods"),
41 punct(':'), punct(':'), ident("set_item"),
42 parens([
43 ident("globals"), punct(','),
44 string(&key), punct(','),
45 TokenTree::Ident(value)
46 ]),
47 punct('.'), ident("expect"), parens([string("python")]),
48 punct(';'),
49 ])),
50 punct(','),
51 punct('|'), ident("e"), punct('|'),
52 punct(':'), punct(':'), ident("std"),
53 punct(':'), punct(':'), ident("panic"),
54 punct(':'), punct(':'), ident("panic_any"),
55 parens([ident("e")]),
56 ]),
57 ]))
58}
59
60fn parens(t: impl IntoIterator<Item = TokenTree>) -> TokenTree {
61 TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::from_iter(t)))
62}
63
64fn compile_to_bytecode(python: &CStr, filename: &CStr, tokens: TokenStream) -> Result<Literal, TokenStream> {
65 Python::with_gil(|py| {
66 let compiled = compile_python(py, python, filename, tokens)?;
67 let bytes = unsafe {
68 let ptr = pyo3::ffi::PyMarshal_WriteObjectToString(compiled.as_ptr(), pyo3::marshal::VERSION);
69 Py::from_owned_ptr(py, ptr)
70 };
71 Ok(Literal::byte_string(bytes.as_bytes(py)))
72 })
73}