Maturin Import Hook

0.3.0 · active · verified Thu Apr 16

The maturin-import-hook library provides a Python import hook for projects built with `maturin`, allowing Python code to dynamically load and use Rust modules without needing an `editable` install or pre-building. It simplifies development workflows for mixed Python/Rust projects by building the Rust extension on demand. The current version is 0.3.0, and it generally follows a bug-fix driven release cadence with support for new Python versions.

Common errors

Warnings

Install

Imports

Quickstart

Demonstrates how to use `maturin-import-hook` to dynamically load a Rust module defined in a `maturin` project. It sets up a minimal Rust project with a `pyproject.toml` and `src/lib.rs`, installs the hook, imports the Rust module, calls a function, and then cleans up. This example assumes `maturin` is already installed in your environment.

import maturin_import_hook
import sys
import os
import shutil

# This hook allows Python to find and load Rust modules
# defined in a maturin project within the current directory
# or a parent directory.
maturin_import_hook.install()

# --- Setup a dummy Rust project for demonstration ---
# In a real project, this setup would already exist.
project_name = "my_rust_module"
project_dir = "temp_rust_project"
module_path = os.path.join(project_dir, "src")

# Clean up any previous run's artifacts
if os.path.exists(project_dir):
    shutil.rmtree(project_dir)

os.makedirs(module_path, exist_ok=True)

# Create pyproject.toml
with open(os.path.join(project_dir, "pyproject.toml"), "w") as f:
    f.write(f"""
[project]
name = "{project_name}"
version = "0.1.0"

[tool.maturin]
name = "{project_name}"
bindings = "pyo3"
    """)

# Create Cargo.toml (maturin can infer some parts, but explicit is clearer for quickstart)
with open(os.path.join(project_dir, "Cargo.toml"), "w") as f:
    f.write(f"""
[package]
name = "{project_name}"
version = "0.1.0"
edition = "2021"

[lib]
name = "{project_name}"
crate-type = ["cdylib"]

[dependencies]
pyo3 = {{ version = "0.20", features = ["extension-module"] }}
    """)


# Create src/lib.rs with a simple PyO3 function
with open(os.path.join(module_path, "lib.rs"), "w") as f:
    f.write("""
use pyo3::prelude::*;

#[pyfunction]
fn greet() -> PyResult<String> {
    Ok("Hello from Rust!".to_string())
}

#[pymodule]
fn my_rust_module(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
    m.add_function(wrap_pyfunction!(greet, m)?)?;
    Ok(())
}
        """)

# Change to the project directory so the hook can find pyproject.toml
original_cwd = os.getcwd()
os.chdir(project_dir)

print(f"Attempting to import '{project_name}' from: {os.getcwd()}")

# Now, import the Rust module as if it were a Python module
# The hook will find pyproject.toml and potentially build the module.
try:
    import my_rust_module
    message = my_rust_module.greet()
    print(f"✅ Successfully imported and called Rust module: {message}")
except ImportError as e:
    print(f"❌ Failed to import Rust module: {e}", file=sys.stderr)
    print("Ensure 'maturin' is installed (pip install maturin) and your Rust project is valid.", file=sys.stderr)
except Exception as e:
    print(f"❌ An unexpected error occurred: {e}", file=sys.stderr)
finally:
    # Clean up dummy project and restore original cwd
    os.chdir(original_cwd)
    if os.path.exists(project_dir):
        print(f"Cleaning up temporary project directory: {project_dir}")
        shutil.rmtree(project_dir)

view raw JSON →