PyO3 / PyO3/setuptools-rust

Scaling PyO3 down with single-file Python extensions

Open
#344 7 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
680
Forks
110
Avg merge
3d 18h
Merged PRs (30d)
1

Description

One nice thing about Cython is that you can have a single .pyx file, two lines in setup.py and one line in requirements.txt, and you're good. With PyO3, you need a Cargo.toml, configured a particular way (cdylib), and so it's just a bit more ceremony than Cython for simple cases.

Maturin is great for standalone libraries, but doesn't help simplify the case of adding a quick extension to an existing Python project.

To solve this, one could imagine single-file Rust extension modules, inspired by https://github.com/rust-lang/rfcs/blob/master/text/3424-cargo-script.md (and see the references to existing tools that predate the RFC, mostly for executables). You might then have:

setup.py
mypackage/
    __init__.py
    module.py
    _extension.rs

And the setup.py is modified to point at _extension.rs, much like you would with Cython, rather than pointing at Cargo.toml as it normally does:

from setuptools import setup
from setuptools_rust import Binding, RustSingleFileExtension

setup(
    name="mypackage",
    version="1.0",
    rust_extensions=[RustSingleFileExtension("mypackage._extension", binding=Binding.PyO3)],
    packages=["mypackage"],
    # rust extensions are not zip safe, just like C-extensions.
    zip_safe=False,
)

The _extension.rs might look like this:

//! ```cargo
//! [dependencies]
//! pyo3 = "0.19"
//! ```

use pyo3::prelude::*;

/// Formats the sum of two numbers as string.
#[pyfunction]
fn sum_as_string(a: usize, b: usize) -> PyResult<String> {
    Ok((a + b).to_string())
}

/// A Python module implemented in Rust.
#[pymodule]
fn _extension(_py: Python, m: &PyModule) -> PyResult<()> {
    m.add_function(wrap_pyfunction!(sum_as_string, m)?)?;
    Ok(())
}

Once you start thinking about lock files this starts getting more complicated, they could be auto-generated as mypackage/_extension.rs.lock, and you'd need some way to update them... conceivably could outsource that to Cargo eventually if the feature becomes non-experimental and they are willing to add a new use case.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading the proposed setup.py integration around RustSingleFileExtension and compare it with the existing Cargo.toml-based flow. Review the linked Cargo script RFC and its prior art, then investigate how PyO3 dependencies and the proposed .rs.lock files would be handled. Done would mean a defined, maintainable approach for building a single-file Rust extension in an existing Python package.

Written by the indexing model from the issue text.

Assessment

Tech stack
python, rust
Domain
build-system
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.