How does this interact with Rust memory safety? I am not familiar with the PyToObject trait, so I am guessing that this has some kind of safety wrapper?
In Python, all data (even simple integers) are all allocated on the heap. ToPyObject makes a deep copy of everything, converting everything to `PyObject` on the heap, so all data is owned.
The numpy crate helps if you need to convert large arrays to Python, as numpy does not store every element separately on the heap.
It uses the same Python interpreter, but each macro invocation is considered its own module. A later version of the crate will have the possibility to keep the context around to be re-used by a later invocation such that, for example, you don't need to import things again.
It doesn't translate Python to Rust or anything like that, it uses CPython both to compile to Python bytecode and to run it. It doesn't launch a separate interpreter, the interpreter is used as a library, so runs in the same process.