From b66f539473f7b822a690a940909e38a55ce6e9a5 Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Mon, 2 Aug 2021 22:29:37 +0100 Subject: [PATCH] docs: sync README and lib.rs examples --- .github/workflows/ci.yml | 2 +- Makefile | 2 +- README.md | 30 +++++------- examples/README.md | 13 ++++++ src/lib.rs | 99 ++++++++++++++++++++++++++-------------- 5 files changed, 92 insertions(+), 54 deletions(-) create mode 100644 examples/README.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1e77a171..441e8f29 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -179,7 +179,7 @@ jobs: - name: Test example extension modules shell: bash run: | - for example_dir in examples/*; do + for example_dir in examples/*/; do tox -c $example_dir -e py done env: diff --git a/Makefile b/Makefile index a5948e9a..e2b5e13e 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ fmt: clippy: cargo clippy --features="num-bigint num-complex hashbrown serde" --tests -- -Dwarnings cargo clippy --features="abi3 num-bigint num-complex hashbrown serde" --tests -- -Dwarnings - for example in examples/*; do cargo clippy --manifest-path $$example/Cargo.toml -- -Dwarnings || exit 1; done + for example in examples/*/; do cargo clippy --manifest-path $$example/Cargo.toml -- -Dwarnings || exit 1; done lint: fmt clippy @true diff --git a/README.md b/README.md index f7492ed1..026709cd 100644 --- a/README.md +++ b/README.md @@ -98,13 +98,13 @@ As well as with `maturin`, it is possible to build using [`setuptools-rust`](htt To embed Python into a Rust binary, you need to ensure that your Python installation contains a shared library. The following steps demonstrate how to ensure this (for Ubuntu), and then give some example code which runs an embedded Python interpreter. -To install the Python shared library, if you are on Ubuntu, you can run: +To install the Python shared library on Ubuntu: ```bash sudo apt install python3-dev ``` -Start a new project with `cargo new`. Next, add `pyo3` to your `Cargo.toml` like this: +Start a new project with `cargo new` and add `pyo3` to the `Cargo.toml` like this: ```toml [dependencies.pyo3] @@ -118,24 +118,18 @@ Example program displaying the value of `sys.version` and the current user name: use pyo3::prelude::*; use pyo3::types::IntoPyDict; -fn main() -> Result<(), ()> { +fn main() -> PyResult<()> { Python::with_gil(|py| { - main_(py).map_err(|e| { - // We can't display Python exceptions via std::fmt::Display, - // so print the error here manually. - e.print_and_set_sys_last_vars(py); - }) - }) -} + let sys = py.import("sys")?; + let version: String = sys.get("version")?.extract()?; -fn main_(py: Python) -> PyResult<()> { - let sys = py.import("sys")?; - let version: String = sys.get("version")?.extract()?; - let locals = [("os", py.import("os")?)].into_py_dict(py); - let code = "os.getenv('USER') or os.getenv('USERNAME') or 'Unknown'"; - let user: String = py.eval(code, None, Some(&locals))?.extract()?; - println!("Hello {}, I'm Python {}", user, version); - Ok(()) + let locals = [("os", py.import("os")?)].into_py_dict(py); + let code = "os.getenv('USER') or os.getenv('USERNAME') or 'Unknown'"; + let user: String = py.eval(code, None, Some(&locals))?.extract()?; + + println!("Hello {}, I'm Python {}", user, version); + Ok(()) + }) } ``` diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 00000000..a1e8ee7f --- /dev/null +++ b/examples/README.md @@ -0,0 +1,13 @@ +# PyO3 Examples + +These examples are a collection of toy extension modules built with PyO3. They are all tested using `tox` in PyO3's CI. + +Below is a brief description of each of these: + +| Example | Description | +| ------- | ----------- | +| `maturin-starter` | A template project which is configured to use [`maturin`](https://github.com/PyO3/maturin) for development. | +| `setuptools-rust-starter` | A template project which is configured to use [`setuptools_rust`](https://github.com/PyO3/setuptools-rust/) for development. | +| `word-count` | A quick performance comparison between word counter implementations written in each of Rust and Python. | +| `pyo3-benchmarks` | A project containing some benchmarks of PyO3 functionality called from Python. | +| `pyo3-pytests` | A project containing some tests of PyO3 functionality called from Python. | diff --git a/src/lib.rs b/src/lib.rs index 9f0c6668..8089d503 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -124,13 +124,13 @@ //! //! # Example: Building a native Python module //! -//! To build, test and publish your crate as a Python module, it is recommended that you use -//! [maturin](https://github.com/PyO3/maturin) or -//! [setuptools-rust](https://github.com/PyO3/setuptools-rust). You can also do this manually. See the -//! [Building and Distribution chapter of the guide](https://pyo3.rs/latest/building_and_distribution.html) -//! for more information. +//! PyO3 can be used to generate a native Python module. The easiest way to try this out for the +//! first time is to use [`maturin`](https://github.com/PyO3/maturin). `maturin` is a tool for +//! building and publishing Rust-based Python packages with minimal configuration. The following +//! steps set up some files for an example Python module, install `maturin`, and then show how build +//! and import the Python module. //! -//! Add these files to your crate's root: +//! First, create a new folder (let's call it `string_sum`) containing the following two files: //! //! **`Cargo.toml`** //! @@ -176,30 +176,46 @@ //! } //! ``` //! -//! **`.cargo/config.toml`** -//! ```toml -//! # These flags must be passed to rustc when compiling for macOS -//! # They can be omitted if you pass the flags yourself -//! # or don't care about macOS +//! With those two files in place, now `maturin` needs to be installed. This can be done using +//! Python's package manager `pip`. First, load up a new Python `virtualenv`, and install `maturin` +//! into it: //! -//! [target.x86_64-apple-darwin] -//! rustflags = [ -//! "-C", "link-arg=-undefined", -//! "-C", "link-arg=dynamic_lookup", -//! ] -//! -//! [target.aarch64-apple-darwin] -//! rustflags = [ -//! "-C", "link-arg=-undefined", -//! "-C", "link-arg=dynamic_lookup", -//! ] +//! ```bash +//! $ cd string_sum +//! $ python -m venv .env +//! $ source .env/bin/activate +//! $ pip install maturin //! ``` //! +//! Now build and execute the module: +//! +//! ```bash +//! $ maturin develop +//! # lots of progress output as maturin runs the compilation... +//! $ python +//! >>> import string_sum +//! >>> string_sum.sum_as_string(5, 20) +//! '25' +//! ``` +//! +//! As well as with `maturin`, it is possible to build using +//! [`setuptools-rust`](https://github.com/PyO3/setuptools-rust) or +//! [manually](https://pyo3.rs/latest/building_and_distribution.html#manual-builds). Both offer more +//! flexibility than `maturin` but require further configuration. +//! //! # Example: Using Python from Rust //! -//! You can use PyO3 to call Python functions from Rust. +//! To embed Python into a Rust binary, you need to ensure that your Python installation contains a +//! shared library. The following steps demonstrate how to ensure this (for Ubuntu), and then give +//! some example code which runs an embedded Python interpreter. //! -//! Add `pyo3` to your `Cargo.toml`: +//! To install the Python shared library on Ubuntu: +//! +//! ```bash +//! sudo apt install python3-dev +//! ``` +//! +//! Start a new project with `cargo new` and add `pyo3` to the `Cargo.toml` like this: //! //! ```toml //! [dependencies.pyo3] @@ -210,26 +226,41 @@ //! features = ["auto-initialize"] //! ``` //! -//! Example program displaying the value of `sys.version`: +//! Example program displaying the value of `sys.version` and the current user name: //! //! ```rust //! use pyo3::prelude::*; //! use pyo3::types::IntoPyDict; //! //! fn main() -> PyResult<()> { -//! let gil = Python::acquire_gil(); -//! let py = gil.python(); -//! let sys = py.import("sys")?; -//! let version: String = sys.get("version")?.extract()?; +//! Python::with_gil(|py| { +//! let sys = py.import("sys")?; +//! let version: String = sys.get("version")?.extract()?; //! -//! let locals = [("os", py.import("os")?)].into_py_dict(py); -//! let code = "os.getenv('USER') or os.getenv('USERNAME') or 'Unknown'"; -//! let user: String = py.eval(code, None, Some(&locals))?.extract()?; +//! let locals = [("os", py.import("os")?)].into_py_dict(py); +//! let code = "os.getenv('USER') or os.getenv('USERNAME') or 'Unknown'"; +//! let user: String = py.eval(code, None, Some(&locals))?.extract()?; //! -//! println!("Hello {}, I'm Python {}", user, version); -//! Ok(()) +//! println!("Hello {}, I'm Python {}", user, version); +//! Ok(()) +//! }) //! } //! ``` +//! +//! The guide has [a section](https://pyo3.rs/latest/python_from_rust.html) with lots of examples +//! about this topic. +//! +//! # Other Examples +//! +//! The PyO3 [README](https://github.com/PyO3/pyo3#readme) contains quick-start examples for both +//! using [Rust from Python](https://github.com/PyO3/pyo3#using-rust-from-python) and +//! [Python from Rust](https://github.com/PyO3/pyo3#using-python-from-rust). +//! +//! The PyO3 repository's [examples subdirectory](https://github.com/PyO3/pyo3/tree/main/examples) +//! contains some basic packages to demonstrate usage of PyO3. +//! +//! There are many projects using PyO3 - see a list of some at +//! pub use crate::class::*; pub use crate::conversion::{