Skip to content

Commit c0f756c

Browse files
authored
fixup docs for packaging (#4710)
1 parent 8e9b497 commit c0f756c

File tree

8 files changed

+234
-42
lines changed

8 files changed

+234
-42
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ name = "string_sum"
7171
crate-type = ["cdylib"]
7272

7373
[dependencies]
74-
pyo3 = { version = "0.22.5", features = ["extension-module"] }
74+
pyo3 = { version = "0.23.0", features = ["extension-module"] }
7575
```
7676

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

141141
```toml
142142
[dependencies.pyo3]
143-
version = "0.22.5"
143+
version = "0.23.0"
144144
features = ["auto-initialize"]
145145
```
146146

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
variable::set("PYO3_VERSION", "0.22.5");
1+
variable::set("PYO3_VERSION", "0.23.0");
22
file::rename(".template/Cargo.toml", "Cargo.toml");
33
file::rename(".template/pyproject.toml", "pyproject.toml");
44
file::delete(".template");
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
variable::set("PYO3_VERSION", "0.22.5");
1+
variable::set("PYO3_VERSION", "0.23.0");
22
file::rename(".template/Cargo.toml", "Cargo.toml");
33
file::rename(".template/pyproject.toml", "pyproject.toml");
44
file::delete(".template");
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
variable::set("PYO3_VERSION", "0.22.5");
1+
variable::set("PYO3_VERSION", "0.23.0");
22
file::rename(".template/Cargo.toml", "Cargo.toml");
33
file::rename(".template/plugin_api/Cargo.toml", "plugin_api/Cargo.toml");
44
file::delete(".template");
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
variable::set("PYO3_VERSION", "0.22.5");
1+
variable::set("PYO3_VERSION", "0.23.0");
22
file::rename(".template/Cargo.toml", "Cargo.toml");
33
file::rename(".template/setup.cfg", "setup.cfg");
44
file::delete(".template");
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
variable::set("PYO3_VERSION", "0.22.5");
1+
variable::set("PYO3_VERSION", "0.23.0");
22
file::rename(".template/Cargo.toml", "Cargo.toml");
33
file::rename(".template/pyproject.toml", "pyproject.toml");
44
file::delete(".template");

pyo3-ffi/README.md

Lines changed: 94 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,32 @@ name = "string_sum"
4141
crate-type = ["cdylib"]
4242

4343
[dependencies.pyo3-ffi]
44-
version = "*"
44+
version = "0.23.0"
4545
features = ["extension-module"]
46+
47+
[build-dependencies]
48+
# This is only necessary if you need to configure your build based on
49+
# the Python version or the compile-time configuration for the interpreter.
50+
pyo3_build_config = "0.23.0"
51+
```
52+
53+
If you need to use conditional compilation based on Python version or how
54+
Python was compiled, you need to add `pyo3-build-config` as a
55+
`build-dependency` in your `Cargo.toml` as in the example above and either
56+
create a new `build.rs` file or modify an existing one so that
57+
`pyo3_build_config::use_pyo3_cfgs()` gets called at build time:
58+
59+
**`build.rs`**
60+
61+
```rust,ignore
62+
fn main() {
63+
pyo3_build_config::use_pyo3_cfgs()
64+
}
4665
```
4766

4867
**`src/lib.rs`**
4968
```rust
50-
use std::os::raw::c_char;
69+
use std::os::raw::{c_char, c_long};
5170
use std::ptr;
5271

5372
use pyo3_ffi::*;
@@ -57,14 +76,14 @@ static mut MODULE_DEF: PyModuleDef = PyModuleDef {
5776
m_name: c_str!("string_sum").as_ptr(),
5877
m_doc: c_str!("A Python module written in Rust.").as_ptr(),
5978
m_size: 0,
60-
m_methods: unsafe { METHODS.as_mut_ptr().cast() },
79+
m_methods: unsafe { METHODS as *const [PyMethodDef] as *mut PyMethodDef },
6180
m_slots: std::ptr::null_mut(),
6281
m_traverse: None,
6382
m_clear: None,
6483
m_free: None,
6584
};
6685

67-
static mut METHODS: [PyMethodDef; 2] = [
86+
static mut METHODS: &[PyMethodDef] = &[
6887
PyMethodDef {
6988
ml_name: c_str!("sum_as_string").as_ptr(),
7089
ml_meth: PyMethodDefPointer {
@@ -74,58 +93,99 @@ static mut METHODS: [PyMethodDef; 2] = [
7493
ml_doc: c_str!("returns the sum of two integers as a string").as_ptr(),
7594
},
7695
// A zeroed PyMethodDef to mark the end of the array.
77-
PyMethodDef::zeroed()
96+
PyMethodDef::zeroed(),
7897
];
7998

8099
// The module initialization function, which must be named `PyInit_<your_module>`.
81100
#[allow(non_snake_case)]
82101
#[no_mangle]
83102
pub unsafe extern "C" fn PyInit_string_sum() -> *mut PyObject {
84-
PyModule_Create(ptr::addr_of_mut!(MODULE_DEF))
103+
let module = PyModule_Create(ptr::addr_of_mut!(MODULE_DEF));
104+
if module.is_null() {
105+
return module;
106+
}
107+
#[cfg(Py_GIL_DISABLED)]
108+
{
109+
if PyUnstable_Module_SetGIL(module, Py_MOD_GIL_NOT_USED) < 0 {
110+
Py_DECREF(module);
111+
return std::ptr::null_mut();
112+
}
113+
}
114+
module
85115
}
86116

87-
pub unsafe extern "C" fn sum_as_string(
88-
_self: *mut PyObject,
89-
args: *mut *mut PyObject,
90-
nargs: Py_ssize_t,
91-
) -> *mut PyObject {
92-
if nargs != 2 {
93-
PyErr_SetString(
94-
PyExc_TypeError,
95-
c_str!("sum_as_string() expected 2 positional arguments").as_ptr(),
117+
/// A helper to parse function arguments
118+
/// If we used PyO3's proc macros they'd handle all of this boilerplate for us :)
119+
unsafe fn parse_arg_as_i32(obj: *mut PyObject, n_arg: usize) -> Option<i32> {
120+
if PyLong_Check(obj) == 0 {
121+
let msg = format!(
122+
"sum_as_string expected an int for positional argument {}\0",
123+
n_arg
96124
);
97-
return std::ptr::null_mut();
125+
PyErr_SetString(PyExc_TypeError, msg.as_ptr().cast::<c_char>());
126+
return None;
98127
}
99128

100-
let arg1 = *args;
101-
if PyLong_Check(arg1) == 0 {
102-
PyErr_SetString(
103-
PyExc_TypeError,
104-
c_str!("sum_as_string() expected an int for positional argument 1").as_ptr(),
105-
);
106-
return std::ptr::null_mut();
129+
// Let's keep the behaviour consistent on platforms where `c_long` is bigger than 32 bits.
130+
// In particular, it is an i32 on Windows but i64 on most Linux systems
131+
let mut overflow = 0;
132+
let i_long: c_long = PyLong_AsLongAndOverflow(obj, &mut overflow);
133+
134+
#[allow(irrefutable_let_patterns)] // some platforms have c_long equal to i32
135+
if overflow != 0 {
136+
raise_overflowerror(obj);
137+
None
138+
} else if let Ok(i) = i_long.try_into() {
139+
Some(i)
140+
} else {
141+
raise_overflowerror(obj);
142+
None
107143
}
144+
}
108145

109-
let arg1 = PyLong_AsLong(arg1);
110-
if !PyErr_Occurred().is_null() {
111-
return ptr::null_mut();
146+
unsafe fn raise_overflowerror(obj: *mut PyObject) {
147+
let obj_repr = PyObject_Str(obj);
148+
if !obj_repr.is_null() {
149+
let mut size = 0;
150+
let p = PyUnicode_AsUTF8AndSize(obj_repr, &mut size);
151+
if !p.is_null() {
152+
let s = std::str::from_utf8_unchecked(std::slice::from_raw_parts(
153+
p.cast::<u8>(),
154+
size as usize,
155+
));
156+
let msg = format!("cannot fit {} in 32 bits\0", s);
157+
158+
PyErr_SetString(PyExc_OverflowError, msg.as_ptr().cast::<c_char>());
159+
}
160+
Py_DECREF(obj_repr);
112161
}
162+
}
113163

114-
let arg2 = *args.add(1);
115-
if PyLong_Check(arg2) == 0 {
164+
pub unsafe extern "C" fn sum_as_string(
165+
_self: *mut PyObject,
166+
args: *mut *mut PyObject,
167+
nargs: Py_ssize_t,
168+
) -> *mut PyObject {
169+
if nargs != 2 {
116170
PyErr_SetString(
117171
PyExc_TypeError,
118-
c_str!("sum_as_string() expected an int for positional argument 2").as_ptr(),
172+
c_str!("sum_as_string expected 2 positional arguments").as_ptr(),
119173
);
120174
return std::ptr::null_mut();
121175
}
122176

123-
let arg2 = PyLong_AsLong(arg2);
124-
if !PyErr_Occurred().is_null() {
125-
return ptr::null_mut();
126-
}
177+
let (first, second) = (*args, *args.add(1));
178+
179+
let first = match parse_arg_as_i32(first, 1) {
180+
Some(x) => x,
181+
None => return std::ptr::null_mut(),
182+
};
183+
let second = match parse_arg_as_i32(second, 2) {
184+
Some(x) => x,
185+
None => return std::ptr::null_mut(),
186+
};
127187

128-
match arg1.checked_add(arg2) {
188+
match first.checked_add(second) {
129189
Some(sum) => {
130190
let string = sum.to_string();
131191
PyUnicode_FromStringAndSize(string.as_ptr().cast::<c_char>(), string.len() as isize)

pyo3-ffi/src/lib.rs

Lines changed: 133 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,139 @@
130130
//!
131131
//! **`src/lib.rs`**
132132
//! ```rust
133-
#![doc = include_str!("../examples/string-sum/src/lib.rs")]
133+
//! use std::os::raw::{c_char, c_long};
134+
//! use std::ptr;
135+
//!
136+
//! use pyo3_ffi::*;
137+
//!
138+
//! static mut MODULE_DEF: PyModuleDef = PyModuleDef {
139+
//! m_base: PyModuleDef_HEAD_INIT,
140+
//! m_name: c_str!("string_sum").as_ptr(),
141+
//! m_doc: c_str!("A Python module written in Rust.").as_ptr(),
142+
//! m_size: 0,
143+
//! m_methods: unsafe { METHODS as *const [PyMethodDef] as *mut PyMethodDef },
144+
//! m_slots: std::ptr::null_mut(),
145+
//! m_traverse: None,
146+
//! m_clear: None,
147+
//! m_free: None,
148+
//! };
149+
//!
150+
//! static mut METHODS: &[PyMethodDef] = &[
151+
//! PyMethodDef {
152+
//! ml_name: c_str!("sum_as_string").as_ptr(),
153+
//! ml_meth: PyMethodDefPointer {
154+
//! PyCFunctionFast: sum_as_string,
155+
//! },
156+
//! ml_flags: METH_FASTCALL,
157+
//! ml_doc: c_str!("returns the sum of two integers as a string").as_ptr(),
158+
//! },
159+
//! // A zeroed PyMethodDef to mark the end of the array.
160+
//! PyMethodDef::zeroed(),
161+
//! ];
162+
//!
163+
//! // The module initialization function, which must be named `PyInit_<your_module>`.
164+
//! #[allow(non_snake_case)]
165+
//! #[no_mangle]
166+
//! pub unsafe extern "C" fn PyInit_string_sum() -> *mut PyObject {
167+
//! let module = PyModule_Create(ptr::addr_of_mut!(MODULE_DEF));
168+
//! if module.is_null() {
169+
//! return module;
170+
//! }
171+
//! #[cfg(Py_GIL_DISABLED)]
172+
//! {
173+
//! if PyUnstable_Module_SetGIL(module, Py_MOD_GIL_NOT_USED) < 0 {
174+
//! Py_DECREF(module);
175+
//! return std::ptr::null_mut();
176+
//! }
177+
//! }
178+
//! module
179+
//! }
180+
//!
181+
//! /// A helper to parse function arguments
182+
//! /// If we used PyO3's proc macros they'd handle all of this boilerplate for us :)
183+
//! unsafe fn parse_arg_as_i32(obj: *mut PyObject, n_arg: usize) -> Option<i32> {
184+
//! if PyLong_Check(obj) == 0 {
185+
//! let msg = format!(
186+
//! "sum_as_string expected an int for positional argument {}\0",
187+
//! n_arg
188+
//! );
189+
//! PyErr_SetString(PyExc_TypeError, msg.as_ptr().cast::<c_char>());
190+
//! return None;
191+
//! }
192+
//!
193+
//! // Let's keep the behaviour consistent on platforms where `c_long` is bigger than 32 bits.
194+
//! // In particular, it is an i32 on Windows but i64 on most Linux systems
195+
//! let mut overflow = 0;
196+
//! let i_long: c_long = PyLong_AsLongAndOverflow(obj, &mut overflow);
197+
//!
198+
//! #[allow(irrefutable_let_patterns)] // some platforms have c_long equal to i32
199+
//! if overflow != 0 {
200+
//! raise_overflowerror(obj);
201+
//! None
202+
//! } else if let Ok(i) = i_long.try_into() {
203+
//! Some(i)
204+
//! } else {
205+
//! raise_overflowerror(obj);
206+
//! None
207+
//! }
208+
//! }
209+
//!
210+
//! unsafe fn raise_overflowerror(obj: *mut PyObject) {
211+
//! let obj_repr = PyObject_Str(obj);
212+
//! if !obj_repr.is_null() {
213+
//! let mut size = 0;
214+
//! let p = PyUnicode_AsUTF8AndSize(obj_repr, &mut size);
215+
//! if !p.is_null() {
216+
//! let s = std::str::from_utf8_unchecked(std::slice::from_raw_parts(
217+
//! p.cast::<u8>(),
218+
//! size as usize,
219+
//! ));
220+
//! let msg = format!("cannot fit {} in 32 bits\0", s);
221+
//!
222+
//! PyErr_SetString(PyExc_OverflowError, msg.as_ptr().cast::<c_char>());
223+
//! }
224+
//! Py_DECREF(obj_repr);
225+
//! }
226+
//! }
227+
//!
228+
//! pub unsafe extern "C" fn sum_as_string(
229+
//! _self: *mut PyObject,
230+
//! args: *mut *mut PyObject,
231+
//! nargs: Py_ssize_t,
232+
//! ) -> *mut PyObject {
233+
//! if nargs != 2 {
234+
//! PyErr_SetString(
235+
//! PyExc_TypeError,
236+
//! c_str!("sum_as_string expected 2 positional arguments").as_ptr(),
237+
//! );
238+
//! return std::ptr::null_mut();
239+
//! }
240+
//!
241+
//! let (first, second) = (*args, *args.add(1));
242+
//!
243+
//! let first = match parse_arg_as_i32(first, 1) {
244+
//! Some(x) => x,
245+
//! None => return std::ptr::null_mut(),
246+
//! };
247+
//! let second = match parse_arg_as_i32(second, 2) {
248+
//! Some(x) => x,
249+
//! None => return std::ptr::null_mut(),
250+
//! };
251+
//!
252+
//! match first.checked_add(second) {
253+
//! Some(sum) => {
254+
//! let string = sum.to_string();
255+
//! PyUnicode_FromStringAndSize(string.as_ptr().cast::<c_char>(), string.len() as isize)
256+
//! }
257+
//! None => {
258+
//! PyErr_SetString(
259+
//! PyExc_OverflowError,
260+
//! c_str!("arguments too large to add").as_ptr(),
261+
//! );
262+
//! std::ptr::null_mut()
263+
//! }
264+
//! }
265+
//! }
134266
//! ```
135267
//!
136268
//! With those two files in place, now `maturin` needs to be installed. This can be done using

0 commit comments

Comments
 (0)