I’m trying to learn how to embed Python into a Rust application. For learning purposes, I want to create a Rust script/app that runs a forever loop. This loop sleeps for a set interval, and upon waking, it uses the Python requests library to fetch the current time from an internet time server. While this isn’t a practical application, my goal is to understand how to call external Python libraries from Rust.
My ultimate goal is to see if I can integrate a Python BACnet stack into a Rust application.
My Setup
<code>use pyo3::prelude::*;
use pyo3::types::IntoPyDict;
fn main() -> PyResult<()> {
// Safely acquire the GIL and run the Python code
// Print the version of Python being used
py.run("import sys; print('Python version:', sys.version)", None, None)?;
// Import the requests library in Python
let requests = py.import("requests")?;
thread::sleep(Duration::from_secs(10));
// Execute the Python code to get the current time from a time server
let locals = [("requests", requests)].into_py_dict(py);
let time_response: String = py.eval(
response = requests.get('http://worldtimeapi.org/api/timezone/Etc/UTC')
response.json()['datetime']
// Print the time received from the server
println!("Current UTC Time: {}", time_response);
<code>use pyo3::prelude::*;
use pyo3::types::IntoPyDict;
use std::thread;
use std::time::Duration;
fn main() -> PyResult<()> {
// Safely acquire the GIL and run the Python code
Python::with_gil(|py| {
// Print the version of Python being used
py.run("import sys; print('Python version:', sys.version)", None, None)?;
// Import the requests library in Python
let requests = py.import("requests")?;
loop {
// Sleep for 10 seconds
thread::sleep(Duration::from_secs(10));
// Execute the Python code to get the current time from a time server
let locals = [("requests", requests)].into_py_dict(py);
let time_response: String = py.eval(
r#"
import requests
response = requests.get('http://worldtimeapi.org/api/timezone/Etc/UTC')
response.json()['datetime']
"#,
None,
Some(locals)
)?.extract()?;
// Print the time received from the server
println!("Current UTC Time: {}", time_response);
}
Ok(())
})
}
</code>
use pyo3::prelude::*;
use pyo3::types::IntoPyDict;
use std::thread;
use std::time::Duration;
fn main() -> PyResult<()> {
// Safely acquire the GIL and run the Python code
Python::with_gil(|py| {
// Print the version of Python being used
py.run("import sys; print('Python version:', sys.version)", None, None)?;
// Import the requests library in Python
let requests = py.import("requests")?;
loop {
// Sleep for 10 seconds
thread::sleep(Duration::from_secs(10));
// Execute the Python code to get the current time from a time server
let locals = [("requests", requests)].into_py_dict(py);
let time_response: String = py.eval(
r#"
import requests
response = requests.get('http://worldtimeapi.org/api/timezone/Etc/UTC')
response.json()['datetime']
"#,
None,
Some(locals)
)?.extract()?;
// Print the time received from the server
println!("Current UTC Time: {}", time_response);
}
Ok(())
})
}
name = "rust_python_time_fetcher"
pyo3 = { version = "0.21.2", features = ["extension-module"] }
pyo3-build-config = "0.21.2"
<code>[package]
name = "rust_python_time_fetcher"
version = "0.1.0"
edition = "2021"
[dependencies]
pyo3 = { version = "0.21.2", features = ["extension-module"] }
[build-dependencies]
pyo3-build-config = "0.21.2"
</code>
[package]
name = "rust_python_time_fetcher"
version = "0.1.0"
edition = "2021"
[dependencies]
pyo3 = { version = "0.21.2", features = ["extension-module"] }
[build-dependencies]
pyo3-build-config = "0.21.2"
# Activate the virtual environment
# Get the path to the Python interpreter
PYTHON_VERSION=$($PYTHON -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')")
# Set the path to the Python interpreter
export PYO3_PYTHON="$PYTHON"
# Set the paths for Python libraries and include files
export LD_LIBRARY_PATH="$($PYTHON -c "import sysconfig; print(sysconfig.get_config_var('LIBDIR'))"):$LD_LIBRARY_PATH"
export LIBRARY_PATH="$($PYTHON -c "import sysconfig; print(sysconfig.get_config_var('LIBDIR'))"):$LIBRARY_PATH"
export PYTHONPATH="$($PYTHON -c "import site; print(site.getsitepackages()[0])"):$PYTHONPATH"
# Include and lib directories might vary based on how Python was installed or the distro specifics
export CFLAGS="$($PYTHON -c "import sysconfig; print('-I' + sysconfig.get_paths()['include'])")"
export LDFLAGS="$($PYTHON -c "import sysconfig; print('-L' + sysconfig.get_config_var('LIBDIR'))")"
# Now try running Cargo build again
# Print Python version and path
# Print Python includes and libs
$PYTHON-config --includes
<code>#!/bin/bash
# Activate the virtual environment
source env/bin/activate
# Get the path to the Python interpreter
PYTHON=$(which python3)
# Get the Python version
PYTHON_VERSION=$($PYTHON -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')")
# Set the path to the Python interpreter
export PYO3_PYTHON="$PYTHON"
# Set the paths for Python libraries and include files
export LD_LIBRARY_PATH="$($PYTHON -c "import sysconfig; print(sysconfig.get_config_var('LIBDIR'))"):$LD_LIBRARY_PATH"
export LIBRARY_PATH="$($PYTHON -c "import sysconfig; print(sysconfig.get_config_var('LIBDIR'))"):$LIBRARY_PATH"
export PYTHONPATH="$($PYTHON -c "import site; print(site.getsitepackages()[0])"):$PYTHONPATH"
# Include and lib directories might vary based on how Python was installed or the distro specifics
export CFLAGS="$($PYTHON -c "import sysconfig; print('-I' + sysconfig.get_paths()['include'])")"
export LDFLAGS="$($PYTHON -c "import sysconfig; print('-L' + sysconfig.get_config_var('LIBDIR'))")"
# Now try running Cargo build again
cargo build
# Print Python version and path
$PYTHON --version
which $PYTHON
# Print Python includes and libs
$PYTHON-config --includes
$PYTHON-config --libs
</code>
#!/bin/bash
# Activate the virtual environment
source env/bin/activate
# Get the path to the Python interpreter
PYTHON=$(which python3)
# Get the Python version
PYTHON_VERSION=$($PYTHON -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')")
# Set the path to the Python interpreter
export PYO3_PYTHON="$PYTHON"
# Set the paths for Python libraries and include files
export LD_LIBRARY_PATH="$($PYTHON -c "import sysconfig; print(sysconfig.get_config_var('LIBDIR'))"):$LD_LIBRARY_PATH"
export LIBRARY_PATH="$($PYTHON -c "import sysconfig; print(sysconfig.get_config_var('LIBDIR'))"):$LIBRARY_PATH"
export PYTHONPATH="$($PYTHON -c "import site; print(site.getsitepackages()[0])"):$PYTHONPATH"
# Include and lib directories might vary based on how Python was installed or the distro specifics
export CFLAGS="$($PYTHON -c "import sysconfig; print('-I' + sysconfig.get_paths()['include'])")"
export LDFLAGS="$($PYTHON -c "import sysconfig; print('-L' + sysconfig.get_config_var('LIBDIR'))")"
# Now try running Cargo build again
cargo build
# Print Python version and path
$PYTHON --version
which $PYTHON
# Print Python includes and libs
$PYTHON-config --includes
$PYTHON-config --libs
The Problem
When I try to build the project with cargo build, I encounter the following error:
Compiling pyo3-build-config v0.21.2
error: failed to run custom build command for `pyo3-build-config v0.21.2`
process didn't exit successfully: `C:UsersbbartlingDesktoprust_python_timetargetdebugbuildpyo3-build-config-6b34c9835096c15dbuild-script-build` (exit code: 1)
cargo:rerun-if-env-changed=PYO3_CONFIG_FILE
cargo:rerun-if-env-changed=PYO3_NO_PYTHON
cargo:rerun-if-env-changed=PYO3_ENVIRONMENT_SIGNATURE
cargo:rerun-if-env-changed=PYO3_PYTHON
cargo:rerun-if-env-changed=VIRTUAL_ENV
cargo:rerun-if-env-changed=CONDA_PREFIX
cargo:rerun-if-env-changed=PATH
error: no Python 3.x interpreter found
<code>> cargo build
Compiling pyo3-build-config v0.21.2
error: failed to run custom build command for `pyo3-build-config v0.21.2`
Caused by:
process didn't exit successfully: `C:UsersbbartlingDesktoprust_python_timetargetdebugbuildpyo3-build-config-6b34c9835096c15dbuild-script-build` (exit code: 1)
--- stdout
cargo:rerun-if-env-changed=PYO3_CONFIG_FILE
cargo:rerun-if-env-changed=PYO3_NO_PYTHON
cargo:rerun-if-env-changed=PYO3_ENVIRONMENT_SIGNATURE
cargo:rerun-if-env-changed=PYO3_PYTHON
cargo:rerun-if-env-changed=VIRTUAL_ENV
cargo:rerun-if-env-changed=CONDA_PREFIX
cargo:rerun-if-env-changed=PATH
--- stderr
error: no Python 3.x interpreter found
</code>
> cargo build
Compiling pyo3-build-config v0.21.2
error: failed to run custom build command for `pyo3-build-config v0.21.2`
Caused by:
process didn't exit successfully: `C:UsersbbartlingDesktoprust_python_timetargetdebugbuildpyo3-build-config-6b34c9835096c15dbuild-script-build` (exit code: 1)
--- stdout
cargo:rerun-if-env-changed=PYO3_CONFIG_FILE
cargo:rerun-if-env-changed=PYO3_NO_PYTHON
cargo:rerun-if-env-changed=PYO3_ENVIRONMENT_SIGNATURE
cargo:rerun-if-env-changed=PYO3_PYTHON
cargo:rerun-if-env-changed=VIRTUAL_ENV
cargo:rerun-if-env-changed=CONDA_PREFIX
cargo:rerun-if-env-changed=PATH
--- stderr
error: no Python 3.x interpreter found
What I’ve Tried
I created a build_with_python.sh
script ran on a rasp pi to set up the environment correctly, including activating a virtual environment and setting the necessary paths for Python libraries and include files. However, I’m still facing the same error.
Questions
- How can I correctly configure my Rust project to recognize and use
the Python interpreter and libraries from my virtual environment?
- Are there any additional steps I need to take to ensure that the pyo3
crate can find and use the Python interpreter?
- Any tips or guidance would be greatly appreciated!