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

fixup docs for packaging #4710

Merged
merged 1 commit into from
Nov 15, 2024
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ name = "string_sum"
crate-type = ["cdylib"]

[dependencies]
pyo3 = { version = "0.22.5", features = ["extension-module"] }
pyo3 = { version = "0.23.0", features = ["extension-module"] }
```

**`src/lib.rs`**
Expand Down Expand Up @@ -140,7 +140,7 @@ Start a new project with `cargo new` and add `pyo3` to the `Cargo.toml` like th

```toml
[dependencies.pyo3]
version = "0.22.5"
version = "0.23.0"
features = ["auto-initialize"]
```

Expand Down
2 changes: 1 addition & 1 deletion examples/decorator/.template/pre-script.rhai
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
variable::set("PYO3_VERSION", "0.22.5");
variable::set("PYO3_VERSION", "0.23.0");
file::rename(".template/Cargo.toml", "Cargo.toml");
file::rename(".template/pyproject.toml", "pyproject.toml");
file::delete(".template");
2 changes: 1 addition & 1 deletion examples/maturin-starter/.template/pre-script.rhai
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
variable::set("PYO3_VERSION", "0.22.5");
variable::set("PYO3_VERSION", "0.23.0");
file::rename(".template/Cargo.toml", "Cargo.toml");
file::rename(".template/pyproject.toml", "pyproject.toml");
file::delete(".template");
2 changes: 1 addition & 1 deletion examples/plugin/.template/pre-script.rhai
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
variable::set("PYO3_VERSION", "0.22.5");
variable::set("PYO3_VERSION", "0.23.0");
file::rename(".template/Cargo.toml", "Cargo.toml");
file::rename(".template/plugin_api/Cargo.toml", "plugin_api/Cargo.toml");
file::delete(".template");
2 changes: 1 addition & 1 deletion examples/setuptools-rust-starter/.template/pre-script.rhai
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
variable::set("PYO3_VERSION", "0.22.5");
variable::set("PYO3_VERSION", "0.23.0");
file::rename(".template/Cargo.toml", "Cargo.toml");
file::rename(".template/setup.cfg", "setup.cfg");
file::delete(".template");
2 changes: 1 addition & 1 deletion examples/word-count/.template/pre-script.rhai
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
variable::set("PYO3_VERSION", "0.22.5");
variable::set("PYO3_VERSION", "0.23.0");
file::rename(".template/Cargo.toml", "Cargo.toml");
file::rename(".template/pyproject.toml", "pyproject.toml");
file::delete(".template");
128 changes: 94 additions & 34 deletions pyo3-ffi/README.md
Copy link
Member Author

@davidhewitt davidhewitt Nov 15, 2024

Choose a reason for hiding this comment

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

Content here had gone stale so I updated it (by copy-paste).

Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,32 @@ name = "string_sum"
crate-type = ["cdylib"]

[dependencies.pyo3-ffi]
version = "*"
version = "0.23.0"
features = ["extension-module"]

[build-dependencies]
# This is only necessary if you need to configure your build based on
# the Python version or the compile-time configuration for the interpreter.
pyo3_build_config = "0.23.0"
```

If you need to use conditional compilation based on Python version or how
Python was compiled, you need to add `pyo3-build-config` as a
`build-dependency` in your `Cargo.toml` as in the example above and either
create a new `build.rs` file or modify an existing one so that
`pyo3_build_config::use_pyo3_cfgs()` gets called at build time:

**`build.rs`**

```rust,ignore
fn main() {
pyo3_build_config::use_pyo3_cfgs()
}
```

**`src/lib.rs`**
```rust
use std::os::raw::c_char;
use std::os::raw::{c_char, c_long};
use std::ptr;

use pyo3_ffi::*;
Expand All @@ -57,14 +76,14 @@ static mut MODULE_DEF: PyModuleDef = PyModuleDef {
m_name: c_str!("string_sum").as_ptr(),
m_doc: c_str!("A Python module written in Rust.").as_ptr(),
m_size: 0,
m_methods: unsafe { METHODS.as_mut_ptr().cast() },
m_methods: unsafe { METHODS as *const [PyMethodDef] as *mut PyMethodDef },
m_slots: std::ptr::null_mut(),
m_traverse: None,
m_clear: None,
m_free: None,
};

static mut METHODS: [PyMethodDef; 2] = [
static mut METHODS: &[PyMethodDef] = &[
PyMethodDef {
ml_name: c_str!("sum_as_string").as_ptr(),
ml_meth: PyMethodDefPointer {
Expand All @@ -74,58 +93,99 @@ static mut METHODS: [PyMethodDef; 2] = [
ml_doc: c_str!("returns the sum of two integers as a string").as_ptr(),
},
// A zeroed PyMethodDef to mark the end of the array.
PyMethodDef::zeroed()
PyMethodDef::zeroed(),
];

// The module initialization function, which must be named `PyInit_<your_module>`.
#[allow(non_snake_case)]
#[no_mangle]
pub unsafe extern "C" fn PyInit_string_sum() -> *mut PyObject {
PyModule_Create(ptr::addr_of_mut!(MODULE_DEF))
let module = PyModule_Create(ptr::addr_of_mut!(MODULE_DEF));
if module.is_null() {
return module;
}
#[cfg(Py_GIL_DISABLED)]
{
if PyUnstable_Module_SetGIL(module, Py_MOD_GIL_NOT_USED) < 0 {
Py_DECREF(module);
return std::ptr::null_mut();
}
}
module
}

pub unsafe extern "C" fn sum_as_string(
_self: *mut PyObject,
args: *mut *mut PyObject,
nargs: Py_ssize_t,
) -> *mut PyObject {
if nargs != 2 {
PyErr_SetString(
PyExc_TypeError,
c_str!("sum_as_string() expected 2 positional arguments").as_ptr(),
/// A helper to parse function arguments
/// If we used PyO3's proc macros they'd handle all of this boilerplate for us :)
unsafe fn parse_arg_as_i32(obj: *mut PyObject, n_arg: usize) -> Option<i32> {
if PyLong_Check(obj) == 0 {
let msg = format!(
"sum_as_string expected an int for positional argument {}\0",
n_arg
);
return std::ptr::null_mut();
PyErr_SetString(PyExc_TypeError, msg.as_ptr().cast::<c_char>());
return None;
}

let arg1 = *args;
if PyLong_Check(arg1) == 0 {
PyErr_SetString(
PyExc_TypeError,
c_str!("sum_as_string() expected an int for positional argument 1").as_ptr(),
);
return std::ptr::null_mut();
// Let's keep the behaviour consistent on platforms where `c_long` is bigger than 32 bits.
// In particular, it is an i32 on Windows but i64 on most Linux systems
let mut overflow = 0;
let i_long: c_long = PyLong_AsLongAndOverflow(obj, &mut overflow);

#[allow(irrefutable_let_patterns)] // some platforms have c_long equal to i32
if overflow != 0 {
raise_overflowerror(obj);
None
} else if let Ok(i) = i_long.try_into() {
Some(i)
} else {
raise_overflowerror(obj);
None
}
}

let arg1 = PyLong_AsLong(arg1);
if !PyErr_Occurred().is_null() {
return ptr::null_mut();
unsafe fn raise_overflowerror(obj: *mut PyObject) {
let obj_repr = PyObject_Str(obj);
if !obj_repr.is_null() {
let mut size = 0;
let p = PyUnicode_AsUTF8AndSize(obj_repr, &mut size);
if !p.is_null() {
let s = std::str::from_utf8_unchecked(std::slice::from_raw_parts(
p.cast::<u8>(),
size as usize,
));
let msg = format!("cannot fit {} in 32 bits\0", s);

PyErr_SetString(PyExc_OverflowError, msg.as_ptr().cast::<c_char>());
}
Py_DECREF(obj_repr);
}
}

let arg2 = *args.add(1);
if PyLong_Check(arg2) == 0 {
pub unsafe extern "C" fn sum_as_string(
_self: *mut PyObject,
args: *mut *mut PyObject,
nargs: Py_ssize_t,
) -> *mut PyObject {
if nargs != 2 {
PyErr_SetString(
PyExc_TypeError,
c_str!("sum_as_string() expected an int for positional argument 2").as_ptr(),
c_str!("sum_as_string expected 2 positional arguments").as_ptr(),
);
return std::ptr::null_mut();
}

let arg2 = PyLong_AsLong(arg2);
if !PyErr_Occurred().is_null() {
return ptr::null_mut();
}
let (first, second) = (*args, *args.add(1));

let first = match parse_arg_as_i32(first, 1) {
Some(x) => x,
None => return std::ptr::null_mut(),
};
let second = match parse_arg_as_i32(second, 2) {
Some(x) => x,
None => return std::ptr::null_mut(),
};

match arg1.checked_add(arg2) {
match first.checked_add(second) {
Some(sum) => {
let string = sum.to_string();
PyUnicode_FromStringAndSize(string.as_ptr().cast::<c_char>(), string.len() as isize)
Expand Down
134 changes: 133 additions & 1 deletion pyo3-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,139 @@
//!
//! **`src/lib.rs`**
//! ```rust
#![doc = include_str!("../examples/string-sum/src/lib.rs")]
//! use std::os::raw::{c_char, c_long};
//! use std::ptr;
//!
//! use pyo3_ffi::*;
//!
//! static mut MODULE_DEF: PyModuleDef = PyModuleDef {
//! m_base: PyModuleDef_HEAD_INIT,
//! m_name: c_str!("string_sum").as_ptr(),
//! m_doc: c_str!("A Python module written in Rust.").as_ptr(),
//! m_size: 0,
//! m_methods: unsafe { METHODS as *const [PyMethodDef] as *mut PyMethodDef },
//! m_slots: std::ptr::null_mut(),
//! m_traverse: None,
//! m_clear: None,
//! m_free: None,
//! };
//!
//! static mut METHODS: &[PyMethodDef] = &[
//! PyMethodDef {
//! ml_name: c_str!("sum_as_string").as_ptr(),
//! ml_meth: PyMethodDefPointer {
//! PyCFunctionFast: sum_as_string,
//! },
//! ml_flags: METH_FASTCALL,
//! ml_doc: c_str!("returns the sum of two integers as a string").as_ptr(),
//! },
//! // A zeroed PyMethodDef to mark the end of the array.
//! PyMethodDef::zeroed(),
//! ];
//!
//! // The module initialization function, which must be named `PyInit_<your_module>`.
//! #[allow(non_snake_case)]
//! #[no_mangle]
//! pub unsafe extern "C" fn PyInit_string_sum() -> *mut PyObject {
//! let module = PyModule_Create(ptr::addr_of_mut!(MODULE_DEF));
//! if module.is_null() {
//! return module;
//! }
//! #[cfg(Py_GIL_DISABLED)]
//! {
//! if PyUnstable_Module_SetGIL(module, Py_MOD_GIL_NOT_USED) < 0 {
//! Py_DECREF(module);
//! return std::ptr::null_mut();
//! }
//! }
//! module
//! }
//!
//! /// A helper to parse function arguments
//! /// If we used PyO3's proc macros they'd handle all of this boilerplate for us :)
//! unsafe fn parse_arg_as_i32(obj: *mut PyObject, n_arg: usize) -> Option<i32> {
//! if PyLong_Check(obj) == 0 {
//! let msg = format!(
//! "sum_as_string expected an int for positional argument {}\0",
//! n_arg
//! );
//! PyErr_SetString(PyExc_TypeError, msg.as_ptr().cast::<c_char>());
//! return None;
//! }
//!
//! // Let's keep the behaviour consistent on platforms where `c_long` is bigger than 32 bits.
//! // In particular, it is an i32 on Windows but i64 on most Linux systems
//! let mut overflow = 0;
//! let i_long: c_long = PyLong_AsLongAndOverflow(obj, &mut overflow);
//!
//! #[allow(irrefutable_let_patterns)] // some platforms have c_long equal to i32
//! if overflow != 0 {
//! raise_overflowerror(obj);
//! None
//! } else if let Ok(i) = i_long.try_into() {
//! Some(i)
//! } else {
//! raise_overflowerror(obj);
//! None
//! }
//! }
//!
//! unsafe fn raise_overflowerror(obj: *mut PyObject) {
//! let obj_repr = PyObject_Str(obj);
//! if !obj_repr.is_null() {
//! let mut size = 0;
//! let p = PyUnicode_AsUTF8AndSize(obj_repr, &mut size);
//! if !p.is_null() {
//! let s = std::str::from_utf8_unchecked(std::slice::from_raw_parts(
//! p.cast::<u8>(),
//! size as usize,
//! ));
//! let msg = format!("cannot fit {} in 32 bits\0", s);
//!
//! PyErr_SetString(PyExc_OverflowError, msg.as_ptr().cast::<c_char>());
//! }
//! Py_DECREF(obj_repr);
//! }
//! }
//!
//! pub unsafe extern "C" fn sum_as_string(
//! _self: *mut PyObject,
//! args: *mut *mut PyObject,
//! nargs: Py_ssize_t,
//! ) -> *mut PyObject {
//! if nargs != 2 {
//! PyErr_SetString(
//! PyExc_TypeError,
//! c_str!("sum_as_string expected 2 positional arguments").as_ptr(),
//! );
//! return std::ptr::null_mut();
//! }
//!
//! let (first, second) = (*args, *args.add(1));
//!
//! let first = match parse_arg_as_i32(first, 1) {
//! Some(x) => x,
//! None => return std::ptr::null_mut(),
//! };
//! let second = match parse_arg_as_i32(second, 2) {
//! Some(x) => x,
//! None => return std::ptr::null_mut(),
//! };
//!
//! match first.checked_add(second) {
//! Some(sum) => {
//! let string = sum.to_string();
//! PyUnicode_FromStringAndSize(string.as_ptr().cast::<c_char>(), string.len() as isize)
//! }
//! None => {
//! PyErr_SetString(
//! PyExc_OverflowError,
//! c_str!("arguments too large to add").as_ptr(),
//! );
//! std::ptr::null_mut()
//! }
//! }
//! }
//! ```
//!
//! With those two files in place, now `maturin` needs to be installed. This can be done using
Expand Down
Loading