File: setuppy_tutorial.md

package info (click to toggle)
python-setuptools-rust 1.9.0%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 648 kB
  • sloc: python: 1,703; javascript: 95; sh: 14; makefile: 13
file content (130 lines) | stat: -rw-r--r-- 5,486 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# Usage with `setup.py`

While `pyproject.toml`-based configuration will be enough for most projects,
sometimes you may need to use custom logic and imperative programming during the build.
For those scenarios, `setuptools` also allows you to specify project configuration
via `setup.py` in addition to `pyproject.toml`.

The following is a very basic tutorial that shows how to use `setuptools-rust` in
your `setup.py`.


## Basic implementation files

Let's start by assuming that you already have a bunch of Python and Rust files[^1]
that you would like to package for distribution in PyPI inside of a project directory
named `hello-world-setuppy`[^2][^3]:

[^1]: To know more about how to write Rust to be integrated into Python packages,
      please have a look on the [PyO3 docs](https://pyo3.rs)
[^2]: You can have a look on the
      [examples/hello-world-setuppy](https://github.com/PyO3/setuptools-rust/tree/main/examples/hello-world-setuppy)
      directory in the `setuptools-rust` repository.
[^3]: If you are an experienced Python or Rust programmer, you may notice that we
      avoid using the `src` directory and explicitly instruct Setuptools and Cargo to
      look into the `python` and `rust` directories respectively.
      Since both Python and Rust ecosystem will try to claim the `src` directory as
      their default, we prefer to be explicit and avoid confusion.


```
hello-world-setuppy
├── Cargo.lock
├── Cargo.toml
├── python
│   └── hello_world
│       └── __init__.py
└── rust
    └── lib.rs
```

```{literalinclude} ../examples/hello-world-setuppy/python/hello_world/__init__.py
   :language: python
```

```{literalinclude} ../examples/hello-world-setuppy/rust/lib.rs
   :language: rust
```

```{literalinclude} ../examples/hello-world-setuppy/Cargo.toml
   :language: toml
```


## Adding files to support packaging

Now we start by adding a `pyproject.toml` which tells anyone that wants to use
our project to use `setuptools` and `setuptools-rust` to build it:

```{literalinclude} ../examples/hello-world-setuppy/pyproject.toml
   :language: toml
```

… and a [`setup.py` configuration file](https://setuptools.pypa.io/en/latest/references/keywords.html)
that tells Setuptools how to build the Rust extensions using our `Cargo.toml` and `setuptools-rust`:

```{literalinclude} ../examples/hello-world-setuppy/setup.py
   :language: python
```

For a complete reference of the options supported by the `RustExtension` class, see the
[API reference](https://setuptools-rust.readthedocs.io/en/latest/reference.html).


We also add a [`MANIFEST.in` file](https://setuptools.pypa.io/en/latest/userguide/miscellaneous.html)
to control which files we want in the source distribution[^4]:

```{literalinclude} ../examples/hello-world-setuppy/MANIFEST.in
```

[^4]: Alternatively you can also use `setuptools-scm` to add all the files under revision control
      to the `sdist`, see the [docs](https://pypi.org/project/setuptools-scm/) for more information.


## Testing the extension

With these files in place, you can install the project in a virtual environment
for testing and making sure everything is working correctly:


```powershell
# cd hello-world-setuppy
python3 -m venv .venv
source .venv/bin/activate  # on Linux or macOS
.venv\Scripts\activate     # on Windows
python -m pip install -e .
python -c 'import hello_world; print(hello_world.sum_as_string(5, 7))'  # => 12
# ... better write some tests with pytest ...
```


## Next steps and final remarks

- When you are ready to distribute your project, have a look on
  [the notes in the documentation about building wheels](https://setuptools-rust.readthedocs.io/en/latest/building_wheels.html).

- You can also use a [`RustBin`](https://setuptools-rust.readthedocs.io/en/latest/reference.html) object
  (instead of a `RustExtension`), if you want to distribute a binary executable
  written in Rust (instead of a library that can be imported by the Python runtime).
  Note however that distributing both library and executable (or multiple executables),
  may significantly increase the size of the
  [wheel](https://packaging.python.org/en/latest/glossary/#term-Wheel)
  file distributed by the
  [package index](https://packaging.python.org/en/latest/glossary/#term-Package-Index)
  and therefore increase build, download and installation times.
  Another approach is to use a Python entry-point that calls the Rust
  implementation (exposed via PyO3 bindings).
  See the [hello-world](https://github.com/PyO3/setuptools-rust/tree/main/examples/hello-world)
  example for more insights.

- If want to include both `RustBin` and `RustExtension` same macOS wheel, you might have
  to manually add an extra `build.rs` file, see [PyO3/setuptools-rust#351](https://github.com/PyO3/setuptools-rust/pull/351)
  for more information about the workaround.

- Since the adoption of {pep}`517`, running `python setup.py ...` directly as a CLI tool is
  [considered deprecated](https://blog.ganssle.io/articles/2021/10/setup-py-deprecated.html).
  Nevertheless, `setup.py` can be safely used as a configuration file
  (the same way `conftest.py` is used by `pytest` or `noxfile.py` is used by `nox`).
  There is a different mindset that comes with this change, though:
  for example, it does not make sense to use `sys.exit(0)` in a `setup.py` file
  or use a overarching `try...except...` block to re-run a failed build with different parameters.