Skip to content

Commit

Permalink
Add mdbook based user guide (#658)
Browse files Browse the repository at this point in the history
* Add mdbook based user guide

* Add netlify config file

* Update guide/src/installation.md

Co-authored-by: David Hewitt <1939362+davidhewitt@users.noreply.github.com>

* Update installation guide

* Update bindings guide

* Update metadata guide

* Add tutorial first draft.

* Add project layout draft.

* Correct informaton about Python type hints in project layout guide.

* Add bindings section draft.

* Update guide/src/bindings.md

* Upgrade mdbook to 0.4.13

* Add repository metadata to mdbook configuration

Co-authored-by: David Hewitt <1939362+davidhewitt@users.noreply.github.com>
Co-authored-by: Ashley Anderson <aanderson@hyperfine-research.com>
  • Loading branch information
3 people authored Nov 6, 2021
1 parent 0dee405 commit 26c0ffb
Show file tree
Hide file tree
Showing 12 changed files with 645 additions and 0 deletions.
1 change: 1 addition & 0 deletions guide/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
book
10 changes: 10 additions & 0 deletions guide/book.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[book]
author = ["PyO3 Project and Contributors"]
language = "en"
multilingual = false
src = "src"
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}"
13 changes: 13 additions & 0 deletions guide/src/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -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)
92 changes: 92 additions & 0 deletions guide/src/bindings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Bindings

Maturin supports several kinds of bindings, some of which are automatically
detected. You can also pass `-b` / `--bindings` command line option to manually
specify which bindings to use.

## `pyo3`

[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"] }
```

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).
## `cffi`

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`.

## `bin`

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`.
3 changes: 3 additions & 0 deletions guide/src/develop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Local Development

TODO
9 changes: 9 additions & 0 deletions guide/src/distribution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Distribution

## Source Distribution

TODO

## Build Wheels

TODO
7 changes: 7 additions & 0 deletions guide/src/index.md
Original file line number Diff line number Diff line change
@@ -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}}
63 changes: 63 additions & 0 deletions guide/src/installation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Installation

## Install from package managers

### PyPI

maturin is published as Python binary wheel to PyPI, you can install it using pip:

```bash
pip install maturin
```

### 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
```

### conda

Installing from the `conda-forge` channel can be achieved by adding `conda-forge` to your conda 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
```

### 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
```
33 changes: 33 additions & 0 deletions guide/src/metadata.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Python Project Metadata

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`.

## 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"]
```
144 changes: 144 additions & 0 deletions guide/src/project_layout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# Project Layout

Maturin expects a particular project layout depending on the contents of the
package.

## Pure Rust project

For a pure Rust project, the structure is as expected and what you get from `cargo new`:

```
my-rust-project/
├── Cargo.toml
├── 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
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
```

> **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

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 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 `<module_name>.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
├── 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
```
Loading

0 comments on commit 26c0ffb

Please sign in to comment.