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

add c_str! macro to create &'static CStr #4255

Merged
merged 12 commits into from
Jun 18, 2024
21 changes: 13 additions & 8 deletions examples/sequential/src/id.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use core::sync::atomic::{AtomicU64, Ordering};
use core::{mem, ptr};
use std::ffi::CString;
use std::os::raw::{c_char, c_int, c_uint, c_ulonglong, c_void};

use pyo3_ffi::*;
Expand Down Expand Up @@ -27,10 +28,10 @@ unsafe extern "C" fn id_new(
kwds: *mut PyObject,
) -> *mut PyObject {
if PyTuple_Size(args) != 0 || !kwds.is_null() {
PyErr_SetString(
PyExc_TypeError,
"Id() takes no arguments\0".as_ptr().cast::<c_char>(),
);
// We use pyo3-ffi's `c_str!` macro to create null-terminated literals because
// Rust's string literals are not null-terminated
// On Rust 1.77 or newer you can use `c"text"` instead.
PyErr_SetString(PyExc_TypeError, c_str!("Id() takes no arguments").as_ptr());
davidhewitt marked this conversation as resolved.
Show resolved Hide resolved
return ptr::null_mut();
}

Expand Down Expand Up @@ -81,8 +82,12 @@ unsafe extern "C" fn id_richcompare(
pyo3_ffi::Py_GT => slf > other,
pyo3_ffi::Py_GE => slf >= other,
unrecognized => {
let msg = format!("unrecognized richcompare opcode {}\0", unrecognized);
PyErr_SetString(PyExc_SystemError, msg.as_ptr().cast::<c_char>());
let msg = CString::new(&*format!(
"unrecognized richcompare opcode {}",
unrecognized
))
.unwrap();
PyErr_SetString(PyExc_SystemError, msg.as_ptr());
return ptr::null_mut();
}
};
Expand All @@ -101,7 +106,7 @@ static mut SLOTS: &[PyType_Slot] = &[
},
PyType_Slot {
slot: Py_tp_doc,
pfunc: "An id that is increased every time an instance is created\0".as_ptr()
pfunc: c_str!("An id that is increased every time an instance is created").as_ptr()
as *mut c_void,
},
PyType_Slot {
Expand All @@ -123,7 +128,7 @@ static mut SLOTS: &[PyType_Slot] = &[
];

pub static mut ID_SPEC: PyType_Spec = PyType_Spec {
name: "sequential.Id\0".as_ptr().cast::<c_char>(),
name: c_str!("sequential.Id").as_ptr(),
basicsize: mem::size_of::<PyId>() as c_int,
itemsize: 0,
flags: (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE) as c_uint,
Expand Down
12 changes: 5 additions & 7 deletions examples/sequential/src/module.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
use core::{mem, ptr};
use pyo3_ffi::*;
use std::os::raw::{c_char, c_int, c_void};
use std::os::raw::{c_int, c_void};

pub static mut MODULE_DEF: PyModuleDef = PyModuleDef {
m_base: PyModuleDef_HEAD_INIT,
m_name: "sequential\0".as_ptr().cast::<c_char>(),
m_doc: "A library for generating sequential ids, written in Rust.\0"
.as_ptr()
.cast::<c_char>(),
m_name: c_str!("sequential").as_ptr(),
m_doc: c_str!("A library for generating sequential ids, written in Rust.").as_ptr(),
m_size: mem::size_of::<sequential_state>() as Py_ssize_t,
m_methods: std::ptr::null_mut(),
m_slots: unsafe { SEQUENTIAL_SLOTS as *const [PyModuleDef_Slot] as *mut PyModuleDef_Slot },
Expand Down Expand Up @@ -42,13 +40,13 @@ unsafe extern "C" fn sequential_exec(module: *mut PyObject) -> c_int {
if id_type.is_null() {
PyErr_SetString(
PyExc_SystemError,
"cannot locate type object\0".as_ptr().cast::<c_char>(),
c_str!("cannot locate type object").as_ptr(),
);
return -1;
}
(*state).id_type = id_type.cast::<PyTypeObject>();

PyModule_AddObjectRef(module, "Id\0".as_ptr().cast::<c_char>(), id_type)
PyModule_AddObjectRef(module, c_str!("Id").as_ptr(), id_type)
}

unsafe extern "C" fn sequential_traverse(
Expand Down
20 changes: 8 additions & 12 deletions examples/sequential/tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ use std::thread;
use pyo3_ffi::*;
use sequential::PyInit_sequential;

static COMMAND: &'static str = "
static COMMAND: &'static str = c_str!(
"
from sequential import Id

s = sum(int(Id()) for _ in range(12))
\0";
"
);

// Newtype to be able to pass it to another thread.
struct State(*mut PyThreadState);
Expand All @@ -19,10 +21,7 @@ unsafe impl Send for State {}
#[test]
fn lets_go_fast() -> Result<(), String> {
unsafe {
let ret = PyImport_AppendInittab(
"sequential\0".as_ptr().cast::<c_char>(),
Some(PyInit_sequential),
);
let ret = PyImport_AppendInittab(c_str!("sequential").as_ptr(), Some(PyInit_sequential));
if ret == -1 {
return Err("could not add module to inittab".into());
}
Expand Down Expand Up @@ -122,11 +121,8 @@ unsafe fn fetch() -> String {

fn run_code() -> Result<u64, String> {
unsafe {
let code_obj = Py_CompileString(
COMMAND.as_ptr().cast::<c_char>(),
"program\0".as_ptr().cast::<c_char>(),
Py_file_input,
);
let code_obj =
Py_CompileString(COMMAND.as_ptr(), c_str!("program").as_ptr(), Py_file_input);
if code_obj.is_null() {
return Err(fetch());
}
Expand All @@ -138,7 +134,7 @@ fn run_code() -> Result<u64, String> {
} else {
Py_DECREF(res_ptr);
}
let sum = PyDict_GetItemString(globals, "s\0".as_ptr().cast::<c_char>()); /* borrowed reference */
let sum = PyDict_GetItemString(globals, c_str!("s").as_ptr()); /* borrowed reference */
if sum.is_null() {
Py_DECREF(globals);
return Err("globals did not have `s`".into());
Expand Down
18 changes: 6 additions & 12 deletions examples/string-sum/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ use pyo3_ffi::*;

static mut MODULE_DEF: PyModuleDef = PyModuleDef {
m_base: PyModuleDef_HEAD_INIT,
m_name: "string_sum\0".as_ptr().cast::<c_char>(),
m_doc: "A Python module written in Rust.\0"
.as_ptr()
.cast::<c_char>(),
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(),
Expand All @@ -19,14 +17,12 @@ static mut MODULE_DEF: PyModuleDef = PyModuleDef {

static mut METHODS: &[PyMethodDef] = &[
PyMethodDef {
ml_name: "sum_as_string\0".as_ptr().cast::<c_char>(),
ml_name: c_str!("sum_as_string").as_ptr(),
ml_meth: PyMethodDefPointer {
_PyCFunctionFast: sum_as_string,
},
ml_flags: METH_FASTCALL,
ml_doc: "returns the sum of two integers as a string\0"
.as_ptr()
.cast::<c_char>(),
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(),
Expand Down Expand Up @@ -93,9 +89,7 @@ pub unsafe extern "C" fn sum_as_string(
if nargs != 2 {
PyErr_SetString(
PyExc_TypeError,
"sum_as_string expected 2 positional arguments\0"
.as_ptr()
.cast::<c_char>(),
c_str!("sum_as_string expected 2 positional arguments").as_ptr(),
);
return std::ptr::null_mut();
}
Expand All @@ -119,7 +113,7 @@ pub unsafe extern "C" fn sum_as_string(
None => {
PyErr_SetString(
PyExc_OverflowError,
"arguments too large to add\0".as_ptr().cast::<c_char>(),
c_str!("arguments too large to add").as_ptr(),
);
std::ptr::null_mut()
}
Expand Down
10 changes: 5 additions & 5 deletions guide/src/class.md
Original file line number Diff line number Diff line change
Expand Up @@ -330,8 +330,8 @@ or [`PyRefMut`] instead of `&mut self`.
Then you can access a parent class by `self_.as_super()` as `&PyRef<Self::BaseClass>`,
or by `self_.into_super()` as `PyRef<Self::BaseClass>` (and similar for the `PyRefMut`
case). For convenience, `self_.as_ref()` can also be used to get `&Self::BaseClass`
directly; however, this approach does not let you access base clases higher in the
inheritance hierarchy, for which you would need to chain multiple `as_super` or
directly; however, this approach does not let you access base clases higher in the
inheritance hierarchy, for which you would need to chain multiple `as_super` or
`into_super` calls.

```rust
Expand Down Expand Up @@ -400,7 +400,7 @@ impl SubSubClass {
let val2 = self_.as_super().val2;
(val1, val2, self_.val3)
}

fn double_values(mut self_: PyRefMut<'_, Self>) {
self_.as_super().as_super().val1 *= 2;
self_.as_super().val2 *= 2;
Expand Down Expand Up @@ -1187,7 +1187,7 @@ Python::with_gil(|py| {
})
```

Ordering of enum variants is optionally added using `#[pyo3(ord)]`.
Ordering of enum variants is optionally added using `#[pyo3(ord)]`.
*Note: Implementation of the `PartialOrd` trait is required when passing the `ord` argument. If not implemented, a compile time error is raised.*

```rust
Expand Down Expand Up @@ -1443,7 +1443,7 @@ impl pyo3::impl_::pyclass::PyClassImpl for MyClass {
static DOC: pyo3::sync::GILOnceCell<::std::borrow::Cow<'static, ::std::ffi::CStr>> = pyo3::sync::GILOnceCell::new();
DOC.get_or_try_init(py, || {
let collector = PyClassImplCollector::<Self>::new();
build_pyclass_doc(<MyClass as pyo3::PyTypeInfo>::NAME, "\0", collector.new_text_signature())
build_pyclass_doc(<MyClass as pyo3::PyTypeInfo>::NAME, pyo3::ffi::c_str!(""), collector.new_text_signature())
}).map(::std::ops::Deref::deref)
}
}
Expand Down
1 change: 1 addition & 0 deletions newsfragments/4255.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add `pyo3_ffi::c_str` macro to create `&'static CStr` on Rust versions which don't have 1.77's `c""` literals.
1 change: 1 addition & 0 deletions newsfragments/4255.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
`PyCFunction::new`, `PyCFunction::new_with_keywords` and `PyCFunction::new_closure` now take `&'static CStr` name and doc arguments (previously was `&'static str`).
26 changes: 8 additions & 18 deletions pyo3-ffi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,8 @@ use pyo3_ffi::*;

static mut MODULE_DEF: PyModuleDef = PyModuleDef {
m_base: PyModuleDef_HEAD_INIT,
m_name: "string_sum\0".as_ptr().cast::<c_char>(),
m_doc: "A Python module written in Rust.\0"
.as_ptr()
.cast::<c_char>(),
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_slots: std::ptr::null_mut(),
Expand All @@ -65,14 +63,12 @@ static mut MODULE_DEF: PyModuleDef = PyModuleDef {

static mut METHODS: [PyMethodDef; 2] = [
PyMethodDef {
ml_name: "sum_as_string\0".as_ptr().cast::<c_char>(),
ml_name: c_str!("sum_as_string").as_ptr(),
ml_meth: PyMethodDefPointer {
_PyCFunctionFast: sum_as_string,
},
ml_flags: METH_FASTCALL,
ml_doc: "returns the sum of two integers as a string\0"
.as_ptr()
.cast::<c_char>(),
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()
Expand All @@ -93,9 +89,7 @@ pub unsafe extern "C" fn sum_as_string(
if nargs != 2 {
PyErr_SetString(
PyExc_TypeError,
"sum_as_string() expected 2 positional arguments\0"
.as_ptr()
.cast::<c_char>(),
c_str!("sum_as_string() expected 2 positional arguments").as_ptr(),
);
return std::ptr::null_mut();
}
Expand All @@ -104,9 +98,7 @@ pub unsafe extern "C" fn sum_as_string(
if PyLong_Check(arg1) == 0 {
PyErr_SetString(
PyExc_TypeError,
"sum_as_string() expected an int for positional argument 1\0"
.as_ptr()
.cast::<c_char>(),
c_str!("sum_as_string() expected an int for positional argument 1").as_ptr(),
);
return std::ptr::null_mut();
}
Expand All @@ -120,9 +112,7 @@ pub unsafe extern "C" fn sum_as_string(
if PyLong_Check(arg2) == 0 {
PyErr_SetString(
PyExc_TypeError,
"sum_as_string() expected an int for positional argument 2\0"
.as_ptr()
.cast::<c_char>(),
c_str!("sum_as_string() expected an int for positional argument 2").as_ptr(),
);
return std::ptr::null_mut();
}
Expand All @@ -140,7 +130,7 @@ pub unsafe extern "C" fn sum_as_string(
None => {
PyErr_SetString(
PyExc_OverflowError,
"arguments too large to add\0".as_ptr().cast::<c_char>(),
c_str!("arguments too large to add").as_ptr(),
);
std::ptr::null_mut()
}
Expand Down
2 changes: 1 addition & 1 deletion pyo3-ffi/src/abstract_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ extern "C" {
#[cfg(not(any(Py_3_8, PyPy)))]
#[inline]
pub unsafe fn PyIter_Check(o: *mut PyObject) -> c_int {
crate::PyObject_HasAttrString(crate::Py_TYPE(o).cast(), "__next__\0".as_ptr().cast())
crate::PyObject_HasAttrString(crate::Py_TYPE(o).cast(), c_str!("__next__").as_ptr())
}

extern "C" {
Expand Down
Loading
Loading