Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

examples: maturin and setuptools_rust examples #1537

Merged
merged 2 commits into from
Apr 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- run: pip install black==19.10b0
- run: pip install black==20.8b1
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
Expand Down Expand Up @@ -130,15 +130,13 @@ jobs:
run: cargo test --manifest-path=pyo3-macros-backend/Cargo.toml

- name: Install python test dependencies
run: |
python -m pip install -U pip setuptools
pip install setuptools-rust pytest pytest-benchmark tox
run: python -m pip install -U pip tox

- name: Test example extension modules
shell: bash
run: |
for example_dir in examples/*; do
tox --discover $(which python) -c $example_dir -e py
tox -c $example_dir -e py
done
env:
TOX_TESTENV_PASSENV: "CARGO_BUILD_TARGET"
Expand Down
6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ nightly = []
members = [
"pyo3-macros",
"pyo3-macros-backend",
"examples/pyo3_benchmarks",
"examples/rustapi_module",
"examples/pyo3-benchmarks",
"examples/pyo3-pytests",
"examples/maturin-starter",
"examples/setuptools-rust-starter",
"examples/word-count"
]
27 changes: 27 additions & 0 deletions examples/maturin-starter/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[package]
authors = ["PyO3 Authors"]
name = "maturin-starter"
version = "0.1.0"
description = "An example project to get started using PyO3 with maturin"
edition = "2018"

[dependencies]

[dependencies.pyo3]
path = "../../"
features = ["extension-module"]

[lib]
name = "maturin_starter"
crate-type = ["cdylib"]

[package.metadata.maturin]
classifier=[
"License :: OSI Approved :: MIT License",
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Rust",
"Operating System :: POSIX",
"Operating System :: MacOS :: MacOS X",
]
24 changes: 24 additions & 0 deletions examples/maturin-starter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# maturin-starter

An example of a basic Python extension module built using PyO3 and [`maturin`](https://github.com/PyO3/maturin).

## Building and Testing

To build this package, first install `maturin`:

```shell
pip install maturin
```

To build and test use `maturin develop`:

```shell
pip install -r requirements-dev.txt
maturin develop && pytest
```

Alternatively, install tox and run the tests inside an isolated environment:

```shell
tox -e py
```
7 changes: 7 additions & 0 deletions examples/maturin-starter/maturin_starter/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# import the contents of the Rust library into the Python extension
from .maturin_starter import *


class PythonClass:
def __init__(self, value: int) -> None:
self.value = value
3 changes: 3 additions & 0 deletions examples/maturin-starter/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["maturin>=0.10,<0.11"]
build-backend = "maturin"
1 change: 1 addition & 0 deletions examples/maturin-starter/requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pytest>=3.5.0
35 changes: 35 additions & 0 deletions examples/maturin-starter/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use pyo3::prelude::*;
use pyo3::types::PyDict;
use pyo3::wrap_pymodule;

mod submodule;
use submodule::*;

#[pyclass]
struct ExampleClass {
#[pyo3(get, set)]
value: i32,
}

#[pymethods]
impl ExampleClass {
#[new]
pub fn new(value: i32) -> Self {
ExampleClass { value }
}
}

#[pymodule]
fn maturin_starter(py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<ExampleClass>()?;
m.add_wrapped(wrap_pymodule!(submodule))?;

// Inserting to sys.modules allows importing submodules nicely from Python
// e.g. from maturin_starter.submodule import SubmoduleClass

let sys = PyModule::import(py, "sys")?;
let sys_modules: &PyDict = sys.getattr("modules")?.downcast()?;
sys_modules.set_item("maturin_starter.submodule", m.getattr("submodule")?)?;

Ok(())
}
22 changes: 22 additions & 0 deletions examples/maturin-starter/src/submodule.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use pyo3::prelude::*;

#[pyclass]
struct SubmoduleClass {}

#[pymethods]
impl SubmoduleClass {
#[new]
pub fn __new__() -> Self {
SubmoduleClass {}
}

pub fn greeting(&self) -> &'static str {
"Hello, world!"
}
}

#[pymodule]
pub fn submodule(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<SubmoduleClass>()?;
Ok(())
}
11 changes: 11 additions & 0 deletions examples/maturin-starter/tests/test_maturin_starter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from maturin_starter import PythonClass, ExampleClass


def test_python_class() -> None:
py_class = PythonClass(value=10)
assert py_class.value == 10


def test_example_class() -> None:
example = ExampleClass(value=11)
assert example.value == 11
6 changes: 6 additions & 0 deletions examples/maturin-starter/tests/test_submodule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from maturin_starter.submodule import SubmoduleClass


def test_submodule_class() -> None:
submodule_class = SubmoduleClass()
assert submodule_class.greeting() == "Hello, world!"
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@ skipsdist = true
description = Run the unit tests under {basepython}
deps = -rrequirements-dev.txt
commands =
python setup.py install
# Use pip master with in-tree-build feature (to be released in pip 21.0)
python -m pip install --upgrade git+https://github.com/pypa/pip.git
python -m pip install . --use-feature=in-tree-build
pytest {posargs}
3 changes: 3 additions & 0 deletions examples/pyo3-benchmarks/MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
include Cargo.toml
recursive-include src *
recursive-include tests
18 changes: 18 additions & 0 deletions examples/pyo3-benchmarks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# pyo3-benchmarks

This extension module contains benchmarks for pieces of PyO3's API accessible from Python.

## Running the benchmarks

You can install the module in your Python environment and then run the benchmarks with pytest:

```shell
python setup.py develop
pytest
```

Or with tox:

```shell
tox -e py
```
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
pip>=19.1
hypothesis>=3.55
pytest>=3.5.0
setuptools-rust>=0.10.2
psutil>=5.6
pytest-benchmark~=3.2
Original file line number Diff line number Diff line change
@@ -1,24 +1,7 @@
import sys
import platform

from setuptools import setup
from setuptools_rust import RustExtension


def get_py_version_cfgs():
# For now each Cfg Py_3_X flag is interpreted as "at least 3.X"
version = sys.version_info[0:2]
py3_min = 6
out_cfg = []
for minor in range(py3_min, version[1] + 1):
out_cfg.append("--cfg=Py_3_%d" % minor)

if platform.python_implementation() == "PyPy":
out_cfg.append("--cfg=PyPy")

return out_cfg


setup(
name="pyo3-benchmarks",
version="0.1.0",
Expand All @@ -35,7 +18,6 @@ def get_py_version_cfgs():
rust_extensions=[
RustExtension(
"pyo3_benchmarks._pyo3_benchmarks",
rustc_flags=get_py_version_cfgs(),
debug=False,
),
],
Expand Down
File renamed without changes.
27 changes: 27 additions & 0 deletions examples/pyo3-pytests/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[package]
authors = ["PyO3 Authors"]
name = "pyo3-pytests"
version = "0.1.0"
description = "Python-based tests for PyO3"
edition = "2018"

[dependencies]

[dependencies.pyo3]
path = "../../"
features = ["extension-module"]

[lib]
name = "pyo3_pytests"
crate-type = ["cdylib"]

[package.metadata.maturin]
classifier=[
"License :: OSI Approved :: MIT License",
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Rust",
"Operating System :: POSIX",
"Operating System :: MacOS :: MacOS X",
]
File renamed without changes.
19 changes: 19 additions & 0 deletions examples/pyo3-pytests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# pyo3-pytests

An extension module built using PyO3, used to test PyO3 from Python.

## Testing

This package is intended to be built using `maturin`. Once built, you can run the tests using `pytest`:

```shell
pip install maturin
maturin develop
pytest
```

Alternatively, install tox and run the tests inside an isolated environment:

```shell
tox -e py
```
28 changes: 28 additions & 0 deletions examples/pyo3-pytests/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use std::process::Command;

fn main() {
let out = Command::new("python")
.args(&["-c", "import sys; import platform; print(sys.version_info[1]); print(platform.python_implementation())"])
.output()
.expect("python version did not print");

let output = String::from_utf8_lossy(&out.stdout);
let mut lines = output.trim().lines();

println!("{}", output);

let version: u8 = lines
.next()
.unwrap()
.parse()
.expect("python version was not parsed");
let implementation = lines.next().unwrap();

for each in 6..version {
println!("cargo:rustc-cfg=Py_3_{}", each);
}

if implementation == "PyPy" {
println!("cargo:rustc-cfg=PyPy");
}
Comment on lines +21 to +27
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This used to be in setup.py, but we now build the tests with maturin instead of setuptools-rust.

We need it because some tests only compile on certain python / PyPy combinations.

IMO this build.rs method is better anyway because it would still work if we changed the examples back to setuptools_rust.

It's a little ugly to have to call a Python interpreter here. I have an idea how to split some of pyo3's build.rs logic into a separate crate pyo3-build-config, which this build.rs could then re-use. I've been torn for a while on whether that refactoring is worth the additional complexity; maybe I should hurry up and push that PR for a while so that others can see what they think.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to move this into a new issue so that it doesn't block this PR with all the new examples, which I think has value even if there's this this wart.

}
1 change: 1 addition & 0 deletions examples/pyo3-pytests/pyo3_pytests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .pyo3_pytests import *
3 changes: 3 additions & 0 deletions examples/pyo3-pytests/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["maturin>=0.10,<0.11"]
build-backend = "maturin"
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
pip>=19.1
hypothesis>=3.55
pytest>=3.5.0
setuptools-rust>=0.10.2
psutil>=5.6
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use pyo3::prelude::*;
use pyo3::types::PyDict;

#[pymodule]
fn test_dict(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
fn dict_iter(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_class::<DictSize>()?;
Ok(())
}
Expand Down
Loading