Merge pull request #1537 from davidhewitt/maturin-examples-v2

examples: maturin and setuptools_rust examples
This commit is contained in:
David Hewitt 2021-04-07 08:05:18 +01:00 committed by GitHub
commit 990bbb5f70
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
67 changed files with 495 additions and 157 deletions

View file

@ -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
@ -141,15 +141,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"

View file

@ -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"
]

View file

@ -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",
]

View file

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

View file

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

View file

@ -0,0 +1,3 @@
[build-system]
requires = ["maturin>=0.10,<0.11"]
build-backend = "maturin"

View file

@ -0,0 +1 @@
pytest>=3.5.0

View file

@ -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(())
}

View file

@ -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(())
}

View file

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

View file

@ -0,0 +1,6 @@
from maturin_starter.submodule import SubmoduleClass
def test_submodule_class() -> None:
submodule_class = SubmoduleClass()
assert submodule_class.greeting() == "Hello, world!"

View file

@ -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}

View file

@ -0,0 +1,3 @@
include Cargo.toml
recursive-include src *
recursive-include tests

View file

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

View file

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

View file

@ -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",
@ -35,7 +18,6 @@ setup(
rust_extensions=[
RustExtension(
"pyo3_benchmarks._pyo3_benchmarks",
rustc_flags=get_py_version_cfgs(),
debug=False,
),
],

View file

@ -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",
]

View file

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

View file

@ -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");
}
}

View file

@ -0,0 +1 @@
from .pyo3_pytests import *

View file

@ -0,0 +1,3 @@
[build-system]
requires = ["maturin>=0.10,<0.11"]
build-backend = "maturin"

View file

@ -1,5 +1,4 @@
pip>=19.1
hypothesis>=3.55
pytest>=3.5.0
setuptools-rust>=0.10.2
psutil>=5.6

View file

@ -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(())
}

View file

@ -0,0 +1,49 @@
use pyo3::prelude::*;
use pyo3::types::PyDict;
use pyo3::wrap_pymodule;
pub mod buf_and_str;
pub mod datetime;
pub mod dict_iter;
pub mod misc;
pub mod objstore;
pub mod othermod;
pub mod pyclass_iter;
pub mod subclassing;
use buf_and_str::*;
use datetime::*;
use dict_iter::*;
use misc::*;
use objstore::*;
use othermod::*;
use pyclass_iter::*;
use subclassing::*;
#[pymodule]
fn pyo3_pytests(py: Python, m: &PyModule) -> PyResult<()> {
m.add_wrapped(wrap_pymodule!(buf_and_str))?;
m.add_wrapped(wrap_pymodule!(datetime))?;
m.add_wrapped(wrap_pymodule!(dict_iter))?;
m.add_wrapped(wrap_pymodule!(misc))?;
m.add_wrapped(wrap_pymodule!(objstore))?;
m.add_wrapped(wrap_pymodule!(othermod))?;
m.add_wrapped(wrap_pymodule!(pyclass_iter))?;
m.add_wrapped(wrap_pymodule!(subclassing))?;
// Inserting to sys.modules allows importing submodules nicely from Python
// e.g. import pyo3_pytests.buf_and_str as bas
let sys = PyModule::import(py, "sys")?;
let sys_modules: &PyDict = sys.getattr("modules")?.downcast()?;
sys_modules.set_item("pyo3_pytests.buf_and_str", m.getattr("buf_and_str")?)?;
sys_modules.set_item("pyo3_pytests.datetime", m.getattr("datetime")?)?;
sys_modules.set_item("pyo3_pytests.dict_iter", m.getattr("dict_iter")?)?;
sys_modules.set_item("pyo3_pytests.misc", m.getattr("misc")?)?;
sys_modules.set_item("pyo3_pytests.objstore", m.getattr("objstore")?)?;
sys_modules.set_item("pyo3_pytests.othermod", m.getattr("othermod")?)?;
sys_modules.set_item("pyo3_pytests.pyclass_iter", m.getattr("pyclass_iter")?)?;
sys_modules.set_item("pyo3_pytests.subclassing", m.getattr("subclassing")?)?;
Ok(())
}

View file

@ -4,7 +4,7 @@ import platform
import psutil
import pytest
from rustapi_module.buf_and_str import BytesExtractor
from pyo3_pytests.buf_and_str import BytesExtractor
PYPY = platform.python_implementation() == "PyPy"

View file

@ -5,7 +5,7 @@ import re
import sys
import pytest
import rustapi_module.datetime as rdt
import pyo3_pytests.datetime as rdt
from hypothesis import given, example
from hypothesis import strategies as st

View file

@ -1,5 +1,5 @@
import pytest
from rustapi_module.test_dict import DictSize
from pyo3_pytests.dict_iter import DictSize
@pytest.mark.parametrize("size", [64, 128, 256])

View file

@ -0,0 +1,6 @@
import pyo3_pytests.misc
def test_issue_219():
# Should not deadlock
pyo3_pytests.misc.issue_219()

View file

@ -4,7 +4,7 @@ import sys
import pytest
from rustapi_module.objstore import ObjStore
from pyo3_pytests.objstore import ObjStore
PYPY = platform.python_implementation() == "PyPy"

View file

@ -1,7 +1,7 @@
from hypothesis import given, assume
from hypothesis import strategies as st
from rustapi_module import othermod
from pyo3_pytests import othermod
INTEGER32_ST = st.integers(min_value=(-(2 ** 31)), max_value=(2 ** 31 - 1))
USIZE_ST = st.integers(min_value=othermod.USIZE_MIN, max_value=othermod.USIZE_MAX)

View file

@ -1,5 +1,5 @@
import pytest
from rustapi_module import pyclass_iter
from pyo3_pytests import pyclass_iter
def test_iter():

View file

@ -1,6 +1,6 @@
import platform
from rustapi_module.subclassing import Subclassable
from pyo3_pytests.subclassing import Subclassable
PYPY = platform.python_implementation() == "PyPy"

View file

@ -0,0 +1,12 @@
[tox]
# can't install from sdist because local pyo3 repo can't be included in the sdist
skipsdist = true
[testenv]
description = Run the unit tests under {basepython}
deps = -rrequirements-dev.txt
commands =
# Use pip master with in-tree-build feature (to be released in pip 21.1)
python -m pip install --upgrade git+https://github.com/pypa/pip.git
python -m pip install . --use-feature=in-tree-build
pytest {posargs}

View file

@ -1,17 +0,0 @@
# rustapi_module
A simple extension module built using PyO3.
## Build
```shell
python setup.py install
```
## Testing
To test install tox globally and run
```shell
tox -e py
```

View file

@ -1,16 +0,0 @@
[package]
authors = ["PyO3 Authors"]
name = "rustapi-module"
version = "0.1.0"
description = "A Python wrapper for the Rust API for purposes of testing"
edition = "2018"
[dependencies]
[dependencies.pyo3]
path = "../../"
features = ["extension-module"]
[lib]
name = "rustapi_module"
crate-type = ["cdylib"]

View file

@ -1,17 +0,0 @@
# rustapi_module
A simple extension module built using PyO3.
## Build
```shell
python setup.py install
```
## Testing
To test install tox globally and run
```shell
tox -e py
```

View file

@ -1,2 +0,0 @@
[build-system]
requires = ["setuptools>=41.0.0", "wheel", "setuptools_rust>=0.10.2"]

View file

@ -1,53 +0,0 @@
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
def make_rust_extension(module_name):
return RustExtension(
module_name, "Cargo.toml", rustc_flags=get_py_version_cfgs(), debug=True
)
setup(
name="rustapi-module",
version="0.1.0",
classifiers=[
"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",
],
packages=["rustapi_module"],
rust_extensions=[
make_rust_extension("rustapi_module.buf_and_str"),
make_rust_extension("rustapi_module.datetime"),
make_rust_extension("rustapi_module.misc"),
make_rust_extension("rustapi_module.objstore"),
make_rust_extension("rustapi_module.othermod"),
make_rust_extension("rustapi_module.pyclass_iter"),
make_rust_extension("rustapi_module.subclassing"),
make_rust_extension("rustapi_module.test_dict"),
],
include_package_data=True,
zip_safe=False,
)

View file

@ -1,8 +0,0 @@
pub mod buf_and_str;
pub mod datetime;
pub mod dict_iter;
pub mod misc;
pub mod objstore;
pub mod othermod;
pub mod pyclass_iter;
pub mod subclassing;

View file

@ -1,6 +0,0 @@
import rustapi_module.misc
def test_issue_219():
# Should not deadlock
rustapi_module.misc.issue_219()

View file

@ -0,0 +1,27 @@
[package]
authors = ["PyO3 Authors"]
name = "setuptools-rust-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 = "setuptools_rust_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",
]

View file

@ -0,0 +1,2 @@
include pyproject.toml Cargo.toml
recursive-include src *

View file

@ -0,0 +1,24 @@
# setuptools-rust-starter
An example of a basic Python extension module built using PyO3 and [`setuptools_rust`](https://github.com/PyO3/setuptools-rust).
## Building and Testing
To build this package, first install `setuptools_rust`:
```shell
pip install setuptools_rust
```
To build and test use `python setup.py develop`:
```shell
pip install -r requirements-dev.txt
python setup.py develop && pytest
```
Alternatively, install tox and run the tests inside an isolated environment:
```shell
tox -e py
```

View file

@ -0,0 +1,2 @@
pytest>=3.5.0
setuptools_rust~=0.11.0

View file

@ -0,0 +1,26 @@
from setuptools import setup
from setuptools_rust import RustExtension
setup(
name="setuptools-rust-starter",
version="0.1.0",
classifiers=[
"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",
],
packages=["setuptools_rust_starter"],
rust_extensions=[
RustExtension(
"setuptools_rust_starter._setuptools_rust_starter",
debug=False,
),
],
include_package_data=True,
zip_safe=False,
)

View file

@ -0,0 +1,7 @@
# import the contents of the Rust library into the Python extension
from ._setuptools_rust_starter import *
class PythonClass:
def __init__(self, value: int) -> None:
self.value = value

View file

@ -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 _setuptools_rust_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 setuptools_rust_starter.submodule import SubmoduleClass
let sys = PyModule::import(py, "sys")?;
let sys_modules: &PyDict = sys.getattr("modules")?.downcast()?;
sys_modules.set_item("setuptools_rust_starter.submodule", m.getattr("submodule")?)?;
Ok(())
}

View file

@ -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(())
}

View file

@ -0,0 +1,11 @@
from setuptools_rust_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

View file

@ -0,0 +1,6 @@
from setuptools_rust_starter.submodule import SubmoduleClass
def test_submodule_class() -> None:
submodule_class = SubmoduleClass()
assert submodule_class.greeting() == "Hello, world!"

View file

@ -0,0 +1,12 @@
[tox]
# can't install from sdist because local pyo3 repo can't be included in the sdist
skipsdist = true
[testenv]
description = Run the unit tests under {basepython}
deps = -rrequirements-dev.txt
commands =
# Use pip master with in-tree-build feature (to be released in pip 21.1)
python -m pip install --upgrade git+https://github.com/pypa/pip.git
python -m pip install . --use-feature=in-tree-build
pytest {posargs}

View file

@ -41,7 +41,7 @@ use crate::{ffi, IntoPy, IntoPyPointer, PyClass, PyObject, Python};
/// # Python::with_gil(|py| {
/// # let inst = Py::new(py, Iter { count: 0 }).unwrap();
/// # pyo3::py_run!(py, inst, "assert next(inst) == 1");
/// # }); // test of StopIteration is done in examples/rustapi_module/pyclass_iter.rs
/// # }); // test of StopIteration is done in examples/pyo3-pytests/pyclass_iter.rs
/// ```
#[allow(unused_variables)]
pub trait PyIterProtocol<'p>: PyClass {