Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can the interpreter be chosen at runtime? #174

Open
mark-summerfield opened this issue Mar 9, 2019 · 4 comments
Open

Can the interpreter be chosen at runtime? #174

mark-summerfield opened this issue Mar 9, 2019 · 4 comments

Comments

@mark-summerfield
Copy link

mark-summerfield commented Mar 9, 2019

Is it possible to specify the interpreter to use, i.e., its path, at runtime?

Something like:

use std::env;
use cpython::...

fn main() {
    let path = env::current_exe().unwrap();
    let interpreter = path.with_file_name(r"python\pythonw.exe");
    Python::set_interpreter(interpreter).unwrap();
    // use cpython as normal
}

The reason for this is if I'm distributing a rust & python app I want to include a python subdir and have rust use that python (with all the extra dependencies I need) rather than any that might happen to be on the system. And, of course, this means that my users don't need to have their own python (e.g., on windows).

@ssokolow
Copy link

ssokolow commented Mar 9, 2019

I'm going to assume you're embedding Python rather than writing a library to be imported by Python.

rust-cpython links against Python as a library in that case. I don't think Python installs its DLLs system-wide, so you probably have to include the requisite Python DLL(s) with your Rust project.

@mark-summerfield
Copy link
Author

Your assumption is correct.
Given a source tree of
myapp/
myapp/src/main.rs
where does python3.dll go? and where does python's libs dir go?
Idealy I'd like:
myapp/target/release/python/ # python3.dll + libs/
So that I can ship release/myapp.exe + release/python/**

@ssokolow
Copy link

Given a source tree of
myapp/
myapp/src/main.rs
where does python3.dll go?

I've never compiled any Rust binaries with dynamic dependencies for Windows, but I think it's safe to assume that Rust+rust-cpython just relies on the OS's dynamic loader. For Windows, that means that any bundled DLLs will need to be in the same folder as the EXE.

(eg. myapp/target/debug/python3.dll or myapp/target/release/python3.dll when using cargo run, assuming that python3.dll actually is the name it's looking for. I'd expect it to include the minor version, for a name like python36.dll, from what I remember of my time on Windows.)

and where does python's libs dir go?

That's determined by the Python import path (sys.path) which, by default with rust-cpython, doesn't include anything specific to your project. It's your job to add an entry.

Here's some Rust code which will show you the default value of sys.path when running Python code via rust-cpython:

use cpython::Python;

fn main() {
    let gil = Python::acquire_gil();
    let py = gil.python();

    let sys = py.import("sys").expect("Cannot import sys");
    let path: Vec<String> = sys
        .get(py, "path").expect("Cannot get sys.path")
        .extract(py).expect("Cannot extract sys.path value");

    println!("{:?}", path);
}

They resolved the potential cyclic dependency between the import path and the sys module by having it built into the DLL, as evidenced by this interpreter session:

>>> import sys
>>> sys
<module 'sys' (built-in)>

As with PATH, Python checks things in sys.path in the order you provide them, so you'll want to insert the path to your bundled imports at the beginning.

(It'd make sense if relative paths are resolved relative to the EXE, rather than the current working directory, for consistency with how import works relative to the .py file containing it, but I've never actually confirmed that since I've always preferred to write compiled extensions for Python applications rather than embedding the Python runtime in a Rust application. You'll want to verify that.)

Another option you might find useful is to include some of your Python code inside the EXE. That'd be done by using include_str! to load a .py file into a const at compile time, then using PyModule::new to create an empty module and using that empty module as the globals value when you feed the const full of code to Python::run.

@indygreg
Copy link
Contributor

indygreg commented Apr 2, 2020

You might be interested in https://pyoxidizer.readthedocs.io/en/stable/ for embedding a Python interpreter in Rust binaries. It aims to solve a lot of these hard packaging problems. You can also look at the source code for the pyembed crate in indygreg/PyOxidizer, which is the component of PyOxidizer interfacing with the Python interpreter. It demonstrates how you can use rust-cpython to manage an embedded Python interpreter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants