From 4d25c6b96e5a4f7135c7ba5f475c36b0abecb0cb Mon Sep 17 00:00:00 2001 From: messense Date: Thu, 2 Sep 2021 23:52:19 +0800 Subject: [PATCH 01/13] Add mdbook based user guide --- guide/.gitignore | 1 + guide/book.toml | 6 +++++ guide/src/SUMMARY.md | 13 +++++++++++ guide/src/bindings.md | 19 ++++++++++++++++ guide/src/develop.md | 3 +++ guide/src/distribution.md | 9 ++++++++ guide/src/index.md | 7 ++++++ guide/src/installation.md | 44 +++++++++++++++++++++++++++++++++++++ guide/src/metadata.md | 5 +++++ guide/src/project_layout.md | 11 ++++++++++ guide/src/tutorial.md | 3 +++ 11 files changed, 121 insertions(+) create mode 100644 guide/.gitignore create mode 100644 guide/book.toml create mode 100644 guide/src/SUMMARY.md create mode 100644 guide/src/bindings.md create mode 100644 guide/src/develop.md create mode 100644 guide/src/distribution.md create mode 100644 guide/src/index.md create mode 100644 guide/src/installation.md create mode 100644 guide/src/metadata.md create mode 100644 guide/src/project_layout.md create mode 100644 guide/src/tutorial.md diff --git a/guide/.gitignore b/guide/.gitignore new file mode 100644 index 000000000..7585238ef --- /dev/null +++ b/guide/.gitignore @@ -0,0 +1 @@ +book diff --git a/guide/book.toml b/guide/book.toml new file mode 100644 index 000000000..12cfa0ad1 --- /dev/null +++ b/guide/book.toml @@ -0,0 +1,6 @@ +[book] +author = ["PyO3 Project and Contributors"] +language = "en" +multilingual = false +src = "src" +title = "maturin user guide" diff --git a/guide/src/SUMMARY.md b/guide/src/SUMMARY.md new file mode 100644 index 000000000..2397fbaa4 --- /dev/null +++ b/guide/src/SUMMARY.md @@ -0,0 +1,13 @@ +# Summary + +[Introduction](index.md) + +--- + +- [Installation](./installation.md) +- [Tutorial](./tutorial.md) +- [Project Layout](./project_layout.md) +- [Bindings](./bindings.md) +- [Python Metadata](./metadata.md) +- [Local Development](./develop.md) +- [Distribution](./distribution.md) diff --git a/guide/src/bindings.md b/guide/src/bindings.md new file mode 100644 index 000000000..b519cc610 --- /dev/null +++ b/guide/src/bindings.md @@ -0,0 +1,19 @@ +# Bindings + +## `pyo3` + +TODO + +mention abi3 support + +## `cffi` + +TODO + +## `rust-cpython` + +TODO + +## `bin` + +TODO diff --git a/guide/src/develop.md b/guide/src/develop.md new file mode 100644 index 000000000..5477bc20f --- /dev/null +++ b/guide/src/develop.md @@ -0,0 +1,3 @@ +# Local Development + +TODO diff --git a/guide/src/distribution.md b/guide/src/distribution.md new file mode 100644 index 000000000..216f342bd --- /dev/null +++ b/guide/src/distribution.md @@ -0,0 +1,9 @@ +# Distribution + +## Source Distribution + +TODO + +## Build Wheels + +TODO diff --git a/guide/src/index.md b/guide/src/index.md new file mode 100644 index 000000000..65dbe95d1 --- /dev/null +++ b/guide/src/index.md @@ -0,0 +1,7 @@ +# Maturin User Guide + +Welcome to the maturin user guide! It contains examples and documentation to explain all of maturin's use cases in detail. + +Please choose from the chapters on the left to jump to individual topics, or continue below to start with maturin's README. + +{{#include ../../README.md}} \ No newline at end of file diff --git a/guide/src/installation.md b/guide/src/installation.md new file mode 100644 index 000000000..1d7be65de --- /dev/null +++ b/guide/src/installation.md @@ -0,0 +1,44 @@ +# Installation + +## Install from PyPI + +maturin is published as Python binary wheel to PyPI, you can install it using pip: + +```bash +pip install maturin +``` + +## Install from Homebrew + +On macOS you can install maturin from Homebrew: + +```bash +brew install maturin +``` + +## Install from GitHub Releases + +You can download maturin binaries from the latest [GitHub Releases](https://github.com/PyO3/maturin/releases/latest). + +## Install from crates.io + +You can install maturin from [crates.io](https://crates.io/crates/maturin) using cargo: + +```bash +cargo install maturin +``` + +## Install from conda-forge + +Installing from the `conda-forge` channel can be achieved by adding `conda-forge` to your channels with: + +``` +conda config --add channels conda-forge +conda config --set channel_priority strict +``` + +Once the `conda-forge` channel has been enabled, `maturin` can be installed with: + +``` +conda install maturin +``` diff --git a/guide/src/metadata.md b/guide/src/metadata.md new file mode 100644 index 000000000..c686795c8 --- /dev/null +++ b/guide/src/metadata.md @@ -0,0 +1,5 @@ +# Python Project Metadata + +## `pyproject.toml` + +TODO diff --git a/guide/src/project_layout.md b/guide/src/project_layout.md new file mode 100644 index 000000000..ae543ef3e --- /dev/null +++ b/guide/src/project_layout.md @@ -0,0 +1,11 @@ +# Project Layout + +mention how to package type stubs + +## Pure Rust Project + +TODO + +## Mixed Rust/Python Project + +TODO diff --git a/guide/src/tutorial.md b/guide/src/tutorial.md new file mode 100644 index 000000000..68db49fa3 --- /dev/null +++ b/guide/src/tutorial.md @@ -0,0 +1,3 @@ +# Tutorial + +Walkthrough a simple pure Rust pyo3 bindings project. From 83af71c200bea4a17101418fe80df67434d22d59 Mon Sep 17 00:00:00 2001 From: messense Date: Fri, 3 Sep 2021 08:25:51 +0800 Subject: [PATCH 02/13] Add netlify config file --- guide/src/index.md | 2 +- netlify.toml | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 netlify.toml diff --git a/guide/src/index.md b/guide/src/index.md index 65dbe95d1..d3ad6a249 100644 --- a/guide/src/index.md +++ b/guide/src/index.md @@ -4,4 +4,4 @@ Welcome to the maturin user guide! It contains examples and documentation to exp Please choose from the chapters on the left to jump to individual topics, or continue below to start with maturin's README. -{{#include ../../README.md}} \ No newline at end of file +{{#include ../../Readme.md}} diff --git a/netlify.toml b/netlify.toml new file mode 100644 index 000000000..cabd94155 --- /dev/null +++ b/netlify.toml @@ -0,0 +1,4 @@ +[build] + base = "guide" + publish = "book/" + command = "curl -L https://github.com/rust-lang/mdBook/releases/download/v0.4.12/mdbook-v0.4.12-x86_64-unknown-linux-gnu.tar.gz | tar xvz && ./mdbook build" From c81e6d9591e5b3dc5b5bea16c358fefcf0be3003 Mon Sep 17 00:00:00 2001 From: messense Date: Fri, 3 Sep 2021 08:50:41 +0800 Subject: [PATCH 03/13] Update guide/src/installation.md Co-authored-by: David Hewitt <1939362+davidhewitt@users.noreply.github.com> --- guide/src/installation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/guide/src/installation.md b/guide/src/installation.md index 1d7be65de..1e946edd9 100644 --- a/guide/src/installation.md +++ b/guide/src/installation.md @@ -28,9 +28,9 @@ You can install maturin from [crates.io](https://crates.io/crates/maturin) using cargo install maturin ``` -## Install from conda-forge +## Install using conda -Installing from the `conda-forge` channel can be achieved by adding `conda-forge` to your channels with: +Installing from the `conda-forge` channel can be achieved by adding `conda-forge` to your conda channels with: ``` conda config --add channels conda-forge From 738ace7d0f9cf25bfb2c0685d57e4f30481dd91e Mon Sep 17 00:00:00 2001 From: messense Date: Fri, 3 Sep 2021 12:10:50 +0800 Subject: [PATCH 04/13] Update installation guide --- guide/src/installation.md | 51 +++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/guide/src/installation.md b/guide/src/installation.md index 1e946edd9..4e5d8ff87 100644 --- a/guide/src/installation.md +++ b/guide/src/installation.md @@ -1,6 +1,8 @@ # Installation -## Install from PyPI +## Install from package managers + +### PyPI maturin is published as Python binary wheel to PyPI, you can install it using pip: @@ -8,27 +10,15 @@ maturin is published as Python binary wheel to PyPI, you can install it using pi pip install maturin ``` -## Install from Homebrew +### Homebrew -On macOS you can install maturin from Homebrew: +On macOS [maturin is in Homebrew](https://formulae.brew.sh/formula/maturin#default) and you can install maturin from Homebrew: ```bash brew install maturin ``` -## Install from GitHub Releases - -You can download maturin binaries from the latest [GitHub Releases](https://github.com/PyO3/maturin/releases/latest). - -## Install from crates.io - -You can install maturin from [crates.io](https://crates.io/crates/maturin) using cargo: - -```bash -cargo install maturin -``` - -## Install using conda +### conda Installing from the `conda-forge` channel can be achieved by adding `conda-forge` to your conda channels with: @@ -42,3 +32,32 @@ Once the `conda-forge` channel has been enabled, `maturin` can be installed with ``` conda install maturin ``` + +### Alpine Linux + +On Alpine Linux, [maturin is in community repository](https://pkgs.alpinelinux.org/packages?name=maturin&branch=edge&repo=community) +and can be installed with `apk` after [enabling the community repository](https://wiki.alpinelinux.org/wiki/Enable_Community_Repository): + +```bash +apk add maturin +``` + +## Download from GitHub Releases + +You can download precompiled maturin binaries from the latest [GitHub Releases](https://github.com/PyO3/maturin/releases/latest). + +## Build from source + +### crates.io + +You can install maturin from [crates.io](https://crates.io/crates/maturin) using cargo: + +```bash +cargo install maturin +``` + +### Git repository + +```bash +cargo install --git https://github.com/PyO3/maturin.git maturin +``` From 28485061cdc57a7f9cc42e851db267d3d426fe6d Mon Sep 17 00:00:00 2001 From: messense Date: Fri, 3 Sep 2021 15:53:24 +0800 Subject: [PATCH 05/13] Update bindings guide --- guide/src/bindings.md | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/guide/src/bindings.md b/guide/src/bindings.md index b519cc610..ebc7f1404 100644 --- a/guide/src/bindings.md +++ b/guide/src/bindings.md @@ -1,10 +1,33 @@ # Bindings +maturin supports several kind of bindings, some of them are automatically +detected. You can also pass `-b` / `--bindings` command line option to manually +specify it. + ## `pyo3` -TODO +[pyo3](https://github.com/PyO3/pyo3) is Rust bindings for Python, +including tools for creating native Python extension modules. +It supports both CPython and PyPy. + +maturin automatically detects pyo3 bindings when it's added as a dependency in `Cargo.toml`. + +### `Py_LIMITED_API`/abi3 + +pyo3 bindings has `Py_LIMITED_API`/abi3 support, enable the `abi3` feature of the `pyo3` crate to use it: + +```toml +pyo3 = { version = "0.14", features = ["abi3"] } +``` -mention abi3 support +> **Note**: Read more about abi3 support in [pyo3's documentation](https://pyo3.rs/latest/building_and_distribution.html#py_limited_apiabi3). + +### Cross Compiling + +pyo3 bindings has decent cross compilation support. +For manylinux support the [manylinux-cross](https://github.com/messense/manylinux-cross) docker images can be used. + +> **Note**: Read more about cross compiling in [pyo3's documentation](https://pyo3.rs/latest/building_and_distribution.html#cross-compiling). ## `cffi` @@ -12,7 +35,10 @@ TODO ## `rust-cpython` -TODO +[rust-cpython](https://github.com/dgrunwald/rust-cpython) is Rust bindings for +the Python interperter. Currently it only supports CPython. + +maturin automatically detects rust-cpython bindings when it's added as a dependency in `Cargo.toml`. ## `bin` From abe7ae7db28e8991a598b906aebbe72bae398ff8 Mon Sep 17 00:00:00 2001 From: messense Date: Fri, 3 Sep 2021 15:59:30 +0800 Subject: [PATCH 06/13] Update metadata guide --- guide/src/metadata.md | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/guide/src/metadata.md b/guide/src/metadata.md index c686795c8..0096779c8 100644 --- a/guide/src/metadata.md +++ b/guide/src/metadata.md @@ -1,5 +1,33 @@ # Python Project Metadata -## `pyproject.toml` +maturin supports [PEP 621](https://www.python.org/dev/peps/pep-0621/), +you can specify python package metadata in `pyproject.toml`. +maturin merges metadata from `Cargo.toml` and `pyproject.toml`, `pyproject.toml` take precedence over `Cargo.toml`. -TODO +## Add Python dependencies + +To specify python dependencies, add a list `dependencies` in a `[project]` section in the `pyproject.toml`. This list is equivalent to `install_requires` in setuptools: + +```toml +[project] +dependencies = ["flask~=1.1.0", "toml==0.10.0"] +``` + +## Add console scripts + +Pip allows adding so called console scripts, which are shell commands that execute some function in you program. You can add console scripts in a section `[project.scripts]`. +The keys are the script names while the values are the path to the function in the format `some.module.path:class.function`, where the `class` part is optional. The function is called with no arguments. Example: + +```toml +[project.scripts] +get_42 = "my_project:DummyClass.get_42" +``` + +## Add trove classifiers + +You can also specify [trove classifiers](https://pypi.org/classifiers/) in your Cargo.toml under `project.classifiers`: + +```toml +[project] +classifiers = ["Programming Language :: Python"] +``` From 91dca536e8826c3340876a1927e242cf0a44a16d Mon Sep 17 00:00:00 2001 From: Ashley Anderson Date: Wed, 27 Oct 2021 13:36:10 -0400 Subject: [PATCH 07/13] Add tutorial first draft. --- guide/src/tutorial.md | 267 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 266 insertions(+), 1 deletion(-) diff --git a/guide/src/tutorial.md b/guide/src/tutorial.md index 68db49fa3..a57ce89b8 100644 --- a/guide/src/tutorial.md +++ b/guide/src/tutorial.md @@ -1,3 +1,268 @@ # Tutorial -Walkthrough a simple pure Rust pyo3 bindings project. +In this tutorial we will wrap a version of [the guessing game from The Rust +Book](https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html) to +run in Python using pyo3. + +## Create a new Rust project + +First, create a new Rust library project using `cargo new --lib --edition 2018 +guessing-game`. This will create a directory with the following structure. + +``` +guessing-game/ +├── Cargo.lock +├── Cargo.toml +└── src + └── lib.rs +``` + +Edit `Cargo.toml` to configure the project and module name, and add the +dependencies (`rand` and `pyo3`). Configure `pyo3` with additional features to +make an extension module compatible with multiple Python versions using the +stable ABI (`abi3`). + +```toml +[package] +name = "guessing-game" +version = "0.1.0" +edition = "2018" + +[lib] +name = "guessing_game" +# "cdylib" is necessary to produce a shared library for Python to import from. +crate-type = ["cdylib"] + +[dependencies] +rand = "0.8.4" + +[dependencies.pyo3] +version = "0.14.5" +# "extension-module" tells pyo3 we want to build an extension module (skips linking against libpython.so) +# "abi3-py36" tells pyo3 (and maturin) to build using the stable ABI with minimum Python version 3.6 +features = ["extension-module", "abi3-py36"] +``` + +## Install and configure maturin (in a virtual environment) + +Create a virtual environment and install maturin. Note maturin has minimal +dependencies! + +```shell +ferris@rustbox [~/src/rust/guessing-game] % python3 -m venv .venv +ferris@rustbox [~/src/rust/guessing-game] % source .venv/bin/activate +(.venv) ferris@rustbox [~/src/rust/guessing-game] % pip install -U pip maturin +(.venv) ferris@rustbox [~/src/rust/guessing-game] % pip freeze +maturin==0.11.5 +toml==0.10.2 +``` + +maturin is configured in `pyproject.toml` as introduced by [PEP +518](https://www.python.org/dev/peps/pep-0518/). This file lives in the root +of your project tree: + +``` +guessing-game/ +├── Cargo.lock +├── Cargo.toml +├── pyproject.toml # <<< add this file +└── src + └── lib.rs +``` + +Configuration in this file is quite simple for most projects. You just need to +indicate maturin as a requirement (and restrict the version) and as the +build-backend (Python supports a number of build-backends since [PEP +517](https://www.python.org/dev/peps/pep-0517/)). + +```toml +[build-system] +requires = ["maturin>=0.11,<0.12"] +build-backend = "maturin" +``` + +Various other tools may also be configured in `pyproject.toml` and the Python +community seems to be consolidating declarative configuration in this file. + +## Program the guessing game in Rust + +When you create a `lib` projectg with `cargo new` it creates a file +`src/lib.rs` with some default code. Edit that file and replace the default +code with the code below. As mentioned, we will implement a slightly +modified version of [the guessing game from The Rust +Book](https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html). +Instead of implemeting as a `bin` crate, we're using a `lib` and will expose +the main logic as a Python function. + +```rust +use pyo3::prelude::*; +use rand::Rng; +use std::cmp::Ordering; +use std::io; + +#[pyfunction] +fn guess_the_number() { + println!("Guess the number!"); + + let secret_number = rand::thread_rng().gen_range(1..101); + + loop { + println!("Please input your guess."); + + let mut guess = String::new(); + + io::stdin() + .read_line(&mut guess) + .expect("Failed to read line"); + + let guess: u32 = match guess.trim().parse() { + Ok(num) => num, + Err(_) => continue, + }; + + println!("You guessed: {}", guess); + + match guess.cmp(&secret_number) { + Ordering::Less => println!("Too small!"), + Ordering::Greater => println!("Too big!"), + Ordering::Equal => { + println!("You win!", guesses); + break; + } + } + } +} + +/// A Python module implemented in Rust. The name of this function must match +/// the `lib.name` setting in the `Cargo.toml`, else Python will not be able to +/// import the module. +#[pymodule] +fn guessing_game(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_function(wrap_pyfunction!(guess_the_number, m)?)?; + + Ok(()) +} +``` + +Thanks to pyo3, there's very little difference between this and the example in +The Rust Book. All we had to do was: +1. Include the pyo3 prelude +2. Add `#[pyfunction]` to our function +3. Add the `#[pymodule]` block to expose the function as part of a Python module + +Refer to the [pyo3 User Guide](https://pyo3.rs/) for more information on using +pyo3. It can do a lot more! + +## Build and install the module with `maturin develop` + +Note that *this is just a Rust project* at this point, and with few exceptions +you can build it as you'd expect using `cargo build`. maturin helps with this, +however, adding some platform-specific build configuration and ultimately +packaging the binary results as a wheel (a `.whl` file, which is an archive of +compiled components suitable for installation with `pip`, the Python package +manager). + +So let's use maturin to build and install in our current environment. + +```shell +(.venv) ferris@rustbox [~/src/rust/guessing-game] % maturin develop +🔗 Found pyo3 bindings with abi3 support for Python ≥ 3.6 +🐍 Not using a specific python interpreter (With abi3, an interpreter is only required on windows) + Compiling libc v0.2.105 + Compiling proc-macro2 v1.0.32 + Compiling cfg-if v1.0.0 + Compiling unicode-xid v0.2.2 + Compiling syn v1.0.81 + Compiling proc-macro-hack v0.5.19 + Compiling pyo3-build-config v0.14.5 + Compiling once_cell v1.8.0 + Compiling parking_lot_core v0.8.5 + Compiling smallvec v1.7.0 + Compiling scopeguard v1.1.0 + Compiling unindent v0.1.7 + Compiling ppv-lite86 v0.2.15 + Compiling instant v0.1.12 + Compiling lock_api v0.4.5 + Compiling getrandom v0.2.3 + Compiling quote v1.0.10 + Compiling rand_core v0.6.3 + Compiling parking_lot v0.11.2 + Compiling paste-impl v0.1.18 + Compiling rand_chacha v0.3.1 + Compiling pyo3 v0.14.5 + Compiling rand v0.8.4 + Compiling paste v0.1.18 + Compiling pyo3-macros-backend v0.14.5 + Compiling indoc-impl v0.3.6 + Compiling indoc v0.3.6 + Compiling pyo3-macros v0.14.5 + Compiling guessing-game v0.1.0 (/Users/ferris/src/rust/guessing-game) + Finished dev [unoptimized + debuginfo] target(s) in 13.31s +``` + +Your `guessing_game` module should now be available in your current virtual +environment. Go ahead and play a few games! +```shell +(.venv) ferris@rustbox [~/src/rust/guessing-game] % python +Python 3.9.6 (default, Aug 25 2021, 16:04:27) +[Clang 12.0.5 (clang-1205.0.22.9)] on darwin +Type "help", "copyright", "credits" or "license" for more information. +>>> import guessing_game +>>> guessing_game.guess_the_number() +Guess the number! +Please input your guess. +42 +You guessed: 42 +Too small! +Please input your guess. +80 +You guessed: 80 +Too big! +Please input your guess. +50 +You guessed: 50 +Too small! +Please input your guess. +60 +You guessed: 60 +Too big! +Please input your guess. +55 +You guessed: 55 +You win! +``` + +## Create a wheel for distribution + +`maturin develop` actually skips the wheel generation part and installs +directly in the current environment. `maturin build` on the other hand will +produce a wheel you can distribute. Note the wheel contains "tags" in its +filename that correspond to supported Python versions, platforms, and/or +architectures, so yours might look a little different. If you want to +distribute broadly, you may need to build on multiple platforms and use a +[`manylinux`](https://github.com/pypa/manylinux) Docker container to build +wheels compatible with a wide range of Linux distros. + +```shell +(.venv) ferris@rustbox [~/src/rust/guessing-game] % maturin build +🔗 Found pyo3 bindings with abi3 support for Python ≥ 3.6 +🐍 Not using a specific python interpreter (With abi3, an interpreter is only required on windows) +📦 Built source distribution to /Users/ferris/src/rust/guessing-game/target/wheels/guessing_game-0.1.0.tar.gz + Compiling pyo3-build-config v0.14.5 + Compiling pyo3-macros-backend v0.14.5 + Compiling pyo3 v0.14.5 + Compiling pyo3-macros v0.14.5 + Compiling guessing-game v0.1.0 (/Users/ferris/src/rust/guessing-game) + Finished dev [unoptimized + debuginfo] target(s) in 7.32s +📦 Built wheel for abi3 Python ≥ 3.6 to /Users/ferris/src/rust/guessing-game/target/wheels/guessing_game-0.1.0-cp36-abi3-macosx_10_7_x86_64.whl +``` + +maturin can even publish wheels directly to [PyPI](https://pypi.org) with +`maturin publish`! + +## Summary +Congratulations! You successfully created a Python module implemented entirely +in Rust thanks to pyo3 and maturin. + +This demonstrates how easy it is to get started with maturin, but keep reading +to learn more about all the additional features. From 46257588428f7b5369d808c7d2870ff5478e750c Mon Sep 17 00:00:00 2001 From: Ashley Anderson Date: Mon, 1 Nov 2021 12:39:00 -0400 Subject: [PATCH 08/13] Add project layout draft. --- guide/src/project_layout.md | 129 ++++++++++++++++++++++++++++++++++-- guide/src/tutorial.md | 2 - 2 files changed, 124 insertions(+), 7 deletions(-) diff --git a/guide/src/project_layout.md b/guide/src/project_layout.md index ae543ef3e..df44e167a 100644 --- a/guide/src/project_layout.md +++ b/guide/src/project_layout.md @@ -1,11 +1,130 @@ # Project Layout -mention how to package type stubs +Maturin expects a particular project layout depending on the contents of the +package. -## Pure Rust Project +## Pure Rust project -TODO +For a pure Rust project, the structure is as expected and what you get from `cargo new`: -## Mixed Rust/Python Project +``` +my-rust-project/ +├── Cargo.toml +├── pyproject.toml # required for maturing configuration +└── src + ├── lib.rs # default for library crates + └── main.rs # default for binary crates +``` -TODO +maturin will add a necessary `__init__.py` to the package when building the +wheel. For convenience, this file includes the following: + +```python +from .my_project import * + +__doc__ = .my_project.__doc__ +``` + +such that the module functions may be called directly with: + +```python +import my_project +my_project.foo() +``` + +rather than: + +```python +from my_project import my_project +``` + +N.B.: there is currently no way to tell maturin to include extra data (e.g. +type stubs or `package_data` in setuptools) for a pure Rust project. Instead, +consider using the layout described below for the mixed Rust/Python project. + +## Mixed Rust/Python project + +To create a mixed Rust/Python project, add a directory with your package name +(i.e. matching `lib.name` in your `Cargo.toml`) to contain the Python source: + +``` +my-rust-and-python-project +├── Cargo.toml +├── my_project # <<< add this directory and put Python code in here +│ ├── __init__.py +│ └── bar.py +├── pyproject.toml +├── Readme.md +└── src + └── lib.rs +``` + +Note that in a mixed Rust/Python project, maturin _does not_ modify the +existing `__init__.py` in the root package, so now to import the rust module in +Python you must use: + +```python +from my_project import my_project +``` + +You can modify `__init__.py` yourself (see above) if you would like to import +functions Rust functions from a higher-level namespace. + +### Alternate Python source directory (src layout) + +Having a directory with `package_name` in the root of the project can +occasionally cause confusion as Python allows importing local packages and +modules. A popular way to avoid this is with the `src`-layout, where the Python +package is nested within a `src` directory. Unfortunately this interferes with +the structure of a typical Rust project. Fortunately, Python is nor particular +about the name of the parent source directory. You tell maturin to use a +different Python source directory in `Cargo.toml` by setting +`package.metadata.maturin.python-source`. For example: + +```toml +[package.metadata.maturin] +python-source = "python" +``` + +then the project structure would look like this: + +``` +my-rust-and-python-project +├── Cargo.toml +├── python +│ └── my_project +│ ├── __init__.py +│ └── bar.py +├── pyproject.toml +├── README.md +└── src + └── lib.rs +``` + +### Adding type information + +The simplest way to add type information for distribution is to use the mixed +Rust/Python projcet layout. In this layout, additional files in the Python +source dir (but not in `.gitignore`) will be automatically included in the +build outputs (source distribution and/or wheel). + +To distribute typing information, you need to add: + +* an empty marker file called `py.typed` in the root of the Python package +* inline types in Python files and/or `.pyi` "stub" files + +``` +my-project +├── Cargo.toml +├── python +│ └── my_project +│ ├── __init__.py +│ ├── py.typed # <<< add this empty file +│ ├── my_project.pyi # <<< add type stubs for Rust functions in the my_project module here +│ ├── bar.pyi # <<< add type stubs for bar.py here OR type bar.py inline +│ └── bar.py +├── pyproject.toml +├── README.md +└── src + └── lib.rs +``` diff --git a/guide/src/tutorial.md b/guide/src/tutorial.md index a57ce89b8..c767ec28d 100644 --- a/guide/src/tutorial.md +++ b/guide/src/tutorial.md @@ -11,7 +11,6 @@ guessing-game`. This will create a directory with the following structure. ``` guessing-game/ -├── Cargo.lock ├── Cargo.toml └── src └── lib.rs @@ -63,7 +62,6 @@ of your project tree: ``` guessing-game/ -├── Cargo.lock ├── Cargo.toml ├── pyproject.toml # <<< add this file └── src From cef650053f3c1aba2207f9ffb79188adaa84024e Mon Sep 17 00:00:00 2001 From: Ashley Anderson Date: Tue, 2 Nov 2021 14:59:18 -0400 Subject: [PATCH 09/13] Correct informaton about Python type hints in project layout guide. --- guide/src/project_layout.md | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/guide/src/project_layout.md b/guide/src/project_layout.md index df44e167a..6d0e36cfc 100644 --- a/guide/src/project_layout.md +++ b/guide/src/project_layout.md @@ -10,13 +10,13 @@ For a pure Rust project, the structure is as expected and what you get from `car ``` my-rust-project/ ├── Cargo.toml -├── pyproject.toml # required for maturing configuration +├── pyproject.toml # required for maturin configuration └── src ├── lib.rs # default for library crates └── main.rs # default for binary crates ``` -maturin will add a necessary `__init__.py` to the package when building the +Maturin will add a necessary `__init__.py` to the package when building the wheel. For convenience, this file includes the following: ```python @@ -38,9 +38,9 @@ rather than: from my_project import my_project ``` -N.B.: there is currently no way to tell maturin to include extra data (e.g. -type stubs or `package_data` in setuptools) for a pure Rust project. Instead, -consider using the layout described below for the mixed Rust/Python project. +> **Note**: there is currently no way to tell maturin to include extra data (e.g. +`package_data` in setuptools) for a pure Rust project. Instead, consider using +the layout described below for the mixed Rust/Python project. ## Mixed Rust/Python project @@ -101,18 +101,32 @@ my-rust-and-python-project └── lib.rs ``` -### Adding type information - -The simplest way to add type information for distribution is to use the mixed -Rust/Python projcet layout. In this layout, additional files in the Python -source dir (but not in `.gitignore`) will be automatically included in the -build outputs (source distribution and/or wheel). +## Adding Python type information To distribute typing information, you need to add: * an empty marker file called `py.typed` in the root of the Python package * inline types in Python files and/or `.pyi` "stub" files +In a pure Rust project, add type stubs in a `.pyi` file in the +project root. Maturin will automatically include this file along with the +required `py.typed` file for you. + +``` +my-rust-project/ +├── Cargo.toml +├── my_project.pyi # <<< add type stubs for Rust functions in the my_project module here +├── pyproject.toml +└── src + └── lib.rs +``` + +In a mixed Rust/Python project, additional files in the Python source dir (but +not in `.gitignore`) will be automatically included in the build outputs +(source distribution and/or wheel). Type information can be therefore added to +the root Python package directory as you might do in a pure Python package. +This requires you to add the `py.typed` marker file yourself. + ``` my-project ├── Cargo.toml From 7874d24b579da1a67483dd66ffb43b9d627b0f68 Mon Sep 17 00:00:00 2001 From: Ashley Anderson Date: Tue, 2 Nov 2021 20:31:15 -0400 Subject: [PATCH 10/13] Add bindings section draft. --- guide/src/bindings.md | 61 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/guide/src/bindings.md b/guide/src/bindings.md index ebc7f1404..8d8c56df5 100644 --- a/guide/src/bindings.md +++ b/guide/src/bindings.md @@ -1,8 +1,8 @@ # Bindings -maturin supports several kind of bindings, some of them are automatically +Maturin supports several kinds of bindings, some of which are automatically detected. You can also pass `-b` / `--bindings` command line option to manually -specify it. +specify which bindings to use. ## `pyo3` @@ -20,26 +20,73 @@ pyo3 bindings has `Py_LIMITED_API`/abi3 support, enable the `abi3` feature of th pyo3 = { version = "0.14", features = ["abi3"] } ``` -> **Note**: Read more about abi3 support in [pyo3's documentation](https://pyo3.rs/latest/building_and_distribution.html#py_limited_apiabi3). +You may additionally specify a minimum Python version by using the `abi3-pyXX` +format for the pyo3 features, where `XX` is corresponds to a Python verison. +For example `abi3-py37` will indicate a minimum Python version of 3.7. + +> **Note**: Read more about abi3 support in [pyo3's +> documentation](https://pyo3.rs/latest/building_and_distribution.html#py_limited_apiabi3). ### Cross Compiling pyo3 bindings has decent cross compilation support. For manylinux support the [manylinux-cross](https://github.com/messense/manylinux-cross) docker images can be used. -> **Note**: Read more about cross compiling in [pyo3's documentation](https://pyo3.rs/latest/building_and_distribution.html#cross-compiling). +> **Note**: Read more about cross compiling in [pyo3's +> documentation](https://pyo3.rs/latest/building_and_distribution.html#cross-compiling). ## `cffi` -TODO +Cffi wheels are compatible with all python versions including pypy. If `cffi` +isn't installed and python is running inside a virtualenv, maturin will install +it, otherwise you have to install it yourself (`pip install cffi`). + +Maturin uses cbindgen to generate a header file for [supported Rust +types](https://github.com/eqrion/cbindgen/blob/master/docs.md#supported-types). +The header file can be customized by configuring cbindgen through a +`cbindgen.toml` file inside your project root. Aternatively you can use a build +script that writes a header file to `$PROJECT_ROOT/target/header.h, like so: + +```rust +use cbindgen; +use std::env; +use std::path::Path; + +fn main() { + let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + + let bindings = cbindgen::Builder::new() + .with_no_includes() + .with_language(cbindgen::Language::C) + .with_crate(crate_dir) + .generate() + .unwrap(); + bindings.write_to_file(Path::new("target").join("header.h")); +} +``` + +Maturin uses the cbindgen-generated header to create a module that exposes `ffi` and +`lib` objects as attributes. See the [cffi docs](https://cffi.readthedocs.io/en/latest/using.html) +for more information on using theses `ffi`/`lib` objects to call the Rust code +from Python. + +> **Note**: Maturin _does not_ automatically detect `cffi` bindings. You _must_ +> specify them via either command line with `-b cffi` or in `pyproject.toml`. ## `rust-cpython` [rust-cpython](https://github.com/dgrunwald/rust-cpython) is Rust bindings for the Python interperter. Currently it only supports CPython. -maturin automatically detects rust-cpython bindings when it's added as a dependency in `Cargo.toml`. +Maturin automatically detects rust-cpython bindings when it's added as a +dependency in `Cargo.toml`. ## `bin` -TODO +Maturin also supports distributing binary applications written in Rust as +Python packages using the `bin` bindings. Binaries are packaged into the wheel +as "scripts" and are available on the user's `PATH` (e.g. in the `bin` +directory of a virtual environment) once installed. + +> **Note**: Maturin _does not_ automatically detect `bin` bindings. You _must_ +> specify them via either command line with `-b bin` or in `pyproject.toml`. From e2a242f1d555290bd2fac911fa453c6266d5cafa Mon Sep 17 00:00:00 2001 From: messense Date: Wed, 3 Nov 2021 11:28:49 +0800 Subject: [PATCH 11/13] Update guide/src/bindings.md --- guide/src/bindings.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guide/src/bindings.md b/guide/src/bindings.md index 8d8c56df5..6fb5a04db 100644 --- a/guide/src/bindings.md +++ b/guide/src/bindings.md @@ -45,7 +45,7 @@ Maturin uses cbindgen to generate a header file for [supported Rust types](https://github.com/eqrion/cbindgen/blob/master/docs.md#supported-types). The header file can be customized by configuring cbindgen through a `cbindgen.toml` file inside your project root. Aternatively you can use a build -script that writes a header file to `$PROJECT_ROOT/target/header.h, like so: +script that writes a header file to `$PROJECT_ROOT/target/header.h`, like so: ```rust use cbindgen; From 3294d58bbd50487d79ae4917095061e0a952cb60 Mon Sep 17 00:00:00 2001 From: messense Date: Wed, 3 Nov 2021 11:29:44 +0800 Subject: [PATCH 12/13] Upgrade mdbook to 0.4.13 --- netlify.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netlify.toml b/netlify.toml index cabd94155..6f84451cc 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,4 +1,4 @@ [build] base = "guide" publish = "book/" - command = "curl -L https://github.com/rust-lang/mdBook/releases/download/v0.4.12/mdbook-v0.4.12-x86_64-unknown-linux-gnu.tar.gz | tar xvz && ./mdbook build" + command = "curl -L https://github.com/rust-lang/mdBook/releases/download/v0.4.13/mdbook-v0.4.13-x86_64-unknown-linux-gnu.tar.gz | tar xvz && ./mdbook build" From 5b6244321c6aab71b8a0df0238e9828264ac353c Mon Sep 17 00:00:00 2001 From: messense Date: Wed, 3 Nov 2021 13:27:09 +0800 Subject: [PATCH 13/13] Add repository metadata to mdbook configuration --- guide/book.toml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/guide/book.toml b/guide/book.toml index 12cfa0ad1..1a759bad8 100644 --- a/guide/book.toml +++ b/guide/book.toml @@ -3,4 +3,8 @@ author = ["PyO3 Project and Contributors"] language = "en" multilingual = false src = "src" -title = "maturin user guide" +title = "Maturin User Guide" + +[output.html] +git-repository-url = "https://github.com/PyO3/maturin/tree/main/guide" +edit-url-template = "https://github.com/PyO3/maturin/edit/main/guide/{path}"