Remove cfg min_const_generics made unnecessary by MSRV bump.
This commit is contained in:
parent
f7bd6ce224
commit
eb04f54683
2
build.rs
2
build.rs
|
@ -46,7 +46,7 @@ fn configure_pyo3() -> Result<()> {
|
|||
|
||||
ensure_auto_initialize_ok(interpreter_config)?;
|
||||
|
||||
// Emit cfgs like `addr_of` and `min_const_generics`
|
||||
// Emit cfgs like `thread_local_const_init`
|
||||
print_feature_cfgs();
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -142,11 +142,6 @@ pub fn print_feature_cfgs() {
|
|||
|
||||
let rustc_minor_version = rustc_minor_version().unwrap_or(0);
|
||||
|
||||
// Enable use of const generics on Rust 1.51 and greater
|
||||
if rustc_minor_version >= 51 {
|
||||
println!("cargo:rustc-cfg=min_const_generics");
|
||||
}
|
||||
|
||||
// Enable use of std::ptr::addr_of! on Rust 1.51 and greater
|
||||
if rustc_minor_version >= 51 {
|
||||
println!("cargo:rustc-cfg=addr_of");
|
||||
|
|
|
@ -100,7 +100,7 @@ fn configure_pyo3() -> Result<()> {
|
|||
println!("{}", line);
|
||||
}
|
||||
|
||||
// Emit cfgs like `addr_of` and `min_const_generics`
|
||||
// Emit cfgs like `thread_local_const_init`
|
||||
print_feature_cfgs();
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -1,308 +1,118 @@
|
|||
use crate::conversion::{AsPyPointer, IntoPyPointer};
|
||||
use crate::types::PySequence;
|
||||
use crate::{exceptions, PyErr};
|
||||
use crate::{
|
||||
ffi, FromPyObject, IntoPy, Py, PyAny, PyDowncastError, PyObject, PyResult, Python, ToPyObject,
|
||||
};
|
||||
|
||||
#[cfg(min_const_generics)]
|
||||
mod min_const_generics {
|
||||
use super::invalid_sequence_length;
|
||||
use crate::conversion::{AsPyPointer, IntoPyPointer};
|
||||
use crate::types::PySequence;
|
||||
use crate::{
|
||||
ffi, FromPyObject, IntoPy, Py, PyAny, PyDowncastError, PyObject, PyResult, Python,
|
||||
ToPyObject,
|
||||
};
|
||||
|
||||
impl<T, const N: usize> IntoPy<PyObject> for [T; N]
|
||||
where
|
||||
T: IntoPy<PyObject>,
|
||||
{
|
||||
fn into_py(self, py: Python<'_>) -> PyObject {
|
||||
unsafe {
|
||||
#[allow(deprecated)] // we're not on edition 2021 yet
|
||||
let elements = std::array::IntoIter::new(self);
|
||||
let len = N as ffi::Py_ssize_t;
|
||||
|
||||
let ptr = ffi::PyList_New(len);
|
||||
|
||||
// We create the `Py` pointer here for two reasons:
|
||||
// - panics if the ptr is null
|
||||
// - its Drop cleans up the list if user code panics.
|
||||
let list: Py<PyAny> = Py::from_owned_ptr(py, ptr);
|
||||
|
||||
for (i, obj) in (0..len).zip(elements) {
|
||||
let obj = obj.into_py(py).into_ptr();
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
ffi::PyList_SET_ITEM(ptr, i, obj);
|
||||
#[cfg(Py_LIMITED_API)]
|
||||
ffi::PyList_SetItem(ptr, i, obj);
|
||||
}
|
||||
|
||||
list
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> ToPyObject for [T; N]
|
||||
where
|
||||
T: ToPyObject,
|
||||
{
|
||||
fn to_object(&self, py: Python<'_>) -> PyObject {
|
||||
self.as_ref().to_object(py)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, const N: usize> FromPyObject<'a> for [T; N]
|
||||
where
|
||||
T: FromPyObject<'a>,
|
||||
{
|
||||
fn extract(obj: &'a PyAny) -> PyResult<Self> {
|
||||
create_array_from_obj(obj)
|
||||
}
|
||||
}
|
||||
|
||||
fn create_array_from_obj<'s, T, const N: usize>(obj: &'s PyAny) -> PyResult<[T; N]>
|
||||
where
|
||||
T: FromPyObject<'s>,
|
||||
{
|
||||
// Types that pass `PySequence_Check` usually implement enough of the sequence protocol
|
||||
// to support this function and if not, we will only fail extraction safely.
|
||||
let seq: &PySequence = unsafe {
|
||||
if ffi::PySequence_Check(obj.as_ptr()) != 0 {
|
||||
obj.downcast_unchecked()
|
||||
} else {
|
||||
return Err(PyDowncastError::new(obj, "Sequence").into());
|
||||
}
|
||||
};
|
||||
let seq_len = seq.len()?;
|
||||
if seq_len != N {
|
||||
return Err(invalid_sequence_length(N, seq_len));
|
||||
}
|
||||
array_try_from_fn(|idx| seq.get_item(idx).and_then(PyAny::extract))
|
||||
}
|
||||
|
||||
// TODO use std::array::try_from_fn, if that stabilises:
|
||||
// (https://github.com/rust-lang/rust/pull/75644)
|
||||
fn array_try_from_fn<E, F, T, const N: usize>(mut cb: F) -> Result<[T; N], E>
|
||||
where
|
||||
F: FnMut(usize) -> Result<T, E>,
|
||||
{
|
||||
// Helper to safely create arrays since the standard library doesn't
|
||||
// provide one yet. Shouldn't be necessary in the future.
|
||||
struct ArrayGuard<T, const N: usize> {
|
||||
dst: *mut T,
|
||||
initialized: usize,
|
||||
}
|
||||
|
||||
impl<T, const N: usize> Drop for ArrayGuard<T, N> {
|
||||
fn drop(&mut self) {
|
||||
debug_assert!(self.initialized <= N);
|
||||
let initialized_part =
|
||||
core::ptr::slice_from_raw_parts_mut(self.dst, self.initialized);
|
||||
unsafe {
|
||||
core::ptr::drop_in_place(initialized_part);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// [MaybeUninit<T>; N] would be "nicer" but is actually difficult to create - there are nightly
|
||||
// APIs which would make this easier.
|
||||
let mut array: core::mem::MaybeUninit<[T; N]> = core::mem::MaybeUninit::uninit();
|
||||
let mut guard: ArrayGuard<T, N> = ArrayGuard {
|
||||
dst: array.as_mut_ptr() as _,
|
||||
initialized: 0,
|
||||
};
|
||||
impl<T, const N: usize> IntoPy<PyObject> for [T; N]
|
||||
where
|
||||
T: IntoPy<PyObject>,
|
||||
{
|
||||
fn into_py(self, py: Python<'_>) -> PyObject {
|
||||
unsafe {
|
||||
let mut value_ptr = array.as_mut_ptr() as *mut T;
|
||||
for i in 0..N {
|
||||
core::ptr::write(value_ptr, cb(i)?);
|
||||
value_ptr = value_ptr.offset(1);
|
||||
guard.initialized += 1;
|
||||
#[allow(deprecated)] // we're not on edition 2021 yet
|
||||
let elements = std::array::IntoIter::new(self);
|
||||
let len = N as ffi::Py_ssize_t;
|
||||
|
||||
let ptr = ffi::PyList_New(len);
|
||||
|
||||
// We create the `Py` pointer here for two reasons:
|
||||
// - panics if the ptr is null
|
||||
// - its Drop cleans up the list if user code panics.
|
||||
let list: Py<PyAny> = Py::from_owned_ptr(py, ptr);
|
||||
|
||||
for (i, obj) in (0..len).zip(elements) {
|
||||
let obj = obj.into_py(py).into_ptr();
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
ffi::PyList_SET_ITEM(ptr, i, obj);
|
||||
#[cfg(Py_LIMITED_API)]
|
||||
ffi::PyList_SetItem(ptr, i, obj);
|
||||
}
|
||||
core::mem::forget(guard);
|
||||
Ok(array.assume_init())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::{
|
||||
panic,
|
||||
sync::atomic::{AtomicUsize, Ordering},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn array_try_from_fn() {
|
||||
static DROP_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||
struct CountDrop;
|
||||
impl Drop for CountDrop {
|
||||
fn drop(&mut self) {
|
||||
DROP_COUNTER.fetch_add(1, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
let _ = catch_unwind_silent(move || {
|
||||
let _: Result<[CountDrop; 4], ()> = super::array_try_from_fn(|idx| {
|
||||
#[allow(clippy::manual_assert)]
|
||||
if idx == 2 {
|
||||
panic!("peek a boo");
|
||||
}
|
||||
Ok(CountDrop)
|
||||
});
|
||||
});
|
||||
assert_eq!(DROP_COUNTER.load(Ordering::SeqCst), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_bytearray_to_array() {
|
||||
Python::with_gil(|py| {
|
||||
let v: [u8; 33] = py
|
||||
.eval(
|
||||
"bytearray(b'abcabcabcabcabcabcabcabcabcabcabc')",
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap()
|
||||
.extract()
|
||||
.unwrap();
|
||||
assert!(&v == b"abcabcabcabcabcabcabcabcabcabcabc");
|
||||
})
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/59211505
|
||||
fn catch_unwind_silent<F, R>(f: F) -> std::thread::Result<R>
|
||||
where
|
||||
F: FnOnce() -> R + panic::UnwindSafe,
|
||||
{
|
||||
let prev_hook = panic::take_hook();
|
||||
panic::set_hook(Box::new(|_| {}));
|
||||
let result = panic::catch_unwind(f);
|
||||
panic::set_hook(prev_hook);
|
||||
result
|
||||
list
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(min_const_generics))]
|
||||
mod array_impls {
|
||||
use super::invalid_sequence_length;
|
||||
use crate::conversion::{AsPyPointer, IntoPyPointer};
|
||||
use crate::types::PySequence;
|
||||
use crate::{
|
||||
ffi, FromPyObject, IntoPy, Py, PyAny, PyDowncastError, PyObject, PyResult, Python,
|
||||
ToPyObject,
|
||||
impl<T, const N: usize> ToPyObject for [T; N]
|
||||
where
|
||||
T: ToPyObject,
|
||||
{
|
||||
fn to_object(&self, py: Python<'_>) -> PyObject {
|
||||
self.as_ref().to_object(py)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, const N: usize> FromPyObject<'a> for [T; N]
|
||||
where
|
||||
T: FromPyObject<'a>,
|
||||
{
|
||||
fn extract(obj: &'a PyAny) -> PyResult<Self> {
|
||||
create_array_from_obj(obj)
|
||||
}
|
||||
}
|
||||
|
||||
fn create_array_from_obj<'s, T, const N: usize>(obj: &'s PyAny) -> PyResult<[T; N]>
|
||||
where
|
||||
T: FromPyObject<'s>,
|
||||
{
|
||||
// Types that pass `PySequence_Check` usually implement enough of the sequence protocol
|
||||
// to support this function and if not, we will only fail extraction safely.
|
||||
let seq: &PySequence = unsafe {
|
||||
if ffi::PySequence_Check(obj.as_ptr()) != 0 {
|
||||
obj.downcast_unchecked()
|
||||
} else {
|
||||
return Err(PyDowncastError::new(obj, "Sequence").into());
|
||||
}
|
||||
};
|
||||
use std::mem::{transmute_copy, ManuallyDrop};
|
||||
let seq_len = seq.len()?;
|
||||
if seq_len != N {
|
||||
return Err(invalid_sequence_length(N, seq_len));
|
||||
}
|
||||
array_try_from_fn(|idx| seq.get_item(idx).and_then(PyAny::extract))
|
||||
}
|
||||
|
||||
macro_rules! array_impls {
|
||||
($($N:expr),+) => {
|
||||
$(
|
||||
impl<T> IntoPy<PyObject> for [T; $N]
|
||||
where
|
||||
T: IntoPy<PyObject>
|
||||
{
|
||||
fn into_py(self, py: Python<'_>) -> PyObject {
|
||||
// TODO use std::array::try_from_fn, if that stabilises:
|
||||
// (https://github.com/rust-lang/rust/pull/75644)
|
||||
fn array_try_from_fn<E, F, T, const N: usize>(mut cb: F) -> Result<[T; N], E>
|
||||
where
|
||||
F: FnMut(usize) -> Result<T, E>,
|
||||
{
|
||||
// Helper to safely create arrays since the standard library doesn't
|
||||
// provide one yet. Shouldn't be necessary in the future.
|
||||
struct ArrayGuard<T, const N: usize> {
|
||||
dst: *mut T,
|
||||
initialized: usize,
|
||||
}
|
||||
|
||||
struct ArrayGuard<T> {
|
||||
elements: [ManuallyDrop<T>; $N],
|
||||
start: usize,
|
||||
}
|
||||
|
||||
impl<T> Drop for ArrayGuard<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let needs_drop = self.elements.get_mut(self.start..).unwrap();
|
||||
for item in needs_drop {
|
||||
ManuallyDrop::drop(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let ptr = ffi::PyList_New($N as ffi::Py_ssize_t);
|
||||
|
||||
// We create the `Py` pointer here for two reasons:
|
||||
// - panics if the ptr is null
|
||||
// - its Drop cleans up the list if user code panics.
|
||||
let list: Py<PyAny> = Py::from_owned_ptr(py, ptr);
|
||||
|
||||
let slf = ManuallyDrop::new(self);
|
||||
|
||||
let mut guard = ArrayGuard {
|
||||
// the transmute size check is _very_ dumb around generics
|
||||
elements: transmute_copy(&slf),
|
||||
start: 0
|
||||
};
|
||||
|
||||
for i in 0..$N {
|
||||
let obj: T = ManuallyDrop::take(&mut guard.elements[i]);
|
||||
guard.start += 1;
|
||||
|
||||
let obj = obj.into_py(py).into_ptr();
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
ffi::PyList_SET_ITEM(ptr, i as ffi::Py_ssize_t, obj);
|
||||
#[cfg(Py_LIMITED_API)]
|
||||
ffi::PyList_SetItem(ptr, i as ffi::Py_ssize_t, obj);
|
||||
}
|
||||
|
||||
std::mem::forget(guard);
|
||||
|
||||
list
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ToPyObject for [T; $N]
|
||||
where
|
||||
T: ToPyObject,
|
||||
{
|
||||
fn to_object(&self, py: Python<'_>) -> PyObject {
|
||||
self.as_ref().to_object(py)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> FromPyObject<'a> for [T; $N]
|
||||
where
|
||||
T: Copy + Default + FromPyObject<'a>,
|
||||
{
|
||||
fn extract(obj: &'a PyAny) -> PyResult<Self> {
|
||||
let mut array = [T::default(); $N];
|
||||
extract_sequence_into_slice(obj, &mut array)?;
|
||||
Ok(array)
|
||||
}
|
||||
}
|
||||
)+
|
||||
impl<T, const N: usize> Drop for ArrayGuard<T, N> {
|
||||
fn drop(&mut self) {
|
||||
debug_assert!(self.initialized <= N);
|
||||
let initialized_part = core::ptr::slice_from_raw_parts_mut(self.dst, self.initialized);
|
||||
unsafe {
|
||||
core::ptr::drop_in_place(initialized_part);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(min_const_generics))]
|
||||
array_impls!(
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
||||
25, 26, 27, 28, 29, 30, 31, 32
|
||||
);
|
||||
|
||||
#[cfg(not(min_const_generics))]
|
||||
fn extract_sequence_into_slice<'s, T>(obj: &'s PyAny, slice: &mut [T]) -> PyResult<()>
|
||||
where
|
||||
T: FromPyObject<'s>,
|
||||
{
|
||||
// Types that pass `PySequence_Check` usually implement enough of the sequence protocol
|
||||
// to support this function and if not, we will only fail extraction safely.
|
||||
let seq: &PySequence = unsafe {
|
||||
if ffi::PySequence_Check(obj.as_ptr()) != 0 {
|
||||
obj.downcast_unchecked()
|
||||
} else {
|
||||
return Err(PyDowncastError::new(obj, "Sequence").into());
|
||||
}
|
||||
};
|
||||
let seq_len = seq.len()?;
|
||||
if seq_len != slice.len() {
|
||||
return Err(invalid_sequence_length(slice.len(), seq_len));
|
||||
// [MaybeUninit<T>; N] would be "nicer" but is actually difficult to create - there are nightly
|
||||
// APIs which would make this easier.
|
||||
let mut array: core::mem::MaybeUninit<[T; N]> = core::mem::MaybeUninit::uninit();
|
||||
let mut guard: ArrayGuard<T, N> = ArrayGuard {
|
||||
dst: array.as_mut_ptr() as _,
|
||||
initialized: 0,
|
||||
};
|
||||
unsafe {
|
||||
let mut value_ptr = array.as_mut_ptr() as *mut T;
|
||||
for i in 0..N {
|
||||
core::ptr::write(value_ptr, cb(i)?);
|
||||
value_ptr = value_ptr.offset(1);
|
||||
guard.initialized += 1;
|
||||
}
|
||||
for (value, item) in slice.iter_mut().zip(seq.iter()?) {
|
||||
*value = item?.extract::<T>()?;
|
||||
}
|
||||
Ok(())
|
||||
core::mem::forget(guard);
|
||||
Ok(array.assume_init())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -315,8 +125,50 @@ fn invalid_sequence_length(expected: usize, actual: usize) -> PyErr {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{
|
||||
panic,
|
||||
sync::atomic::{AtomicUsize, Ordering},
|
||||
};
|
||||
|
||||
use crate::{types::PyList, IntoPy, PyResult, Python, ToPyObject};
|
||||
|
||||
#[test]
|
||||
fn array_try_from_fn() {
|
||||
static DROP_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||
struct CountDrop;
|
||||
impl Drop for CountDrop {
|
||||
fn drop(&mut self) {
|
||||
DROP_COUNTER.fetch_add(1, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
let _ = catch_unwind_silent(move || {
|
||||
let _: Result<[CountDrop; 4], ()> = super::array_try_from_fn(|idx| {
|
||||
#[allow(clippy::manual_assert)]
|
||||
if idx == 2 {
|
||||
panic!("peek a boo");
|
||||
}
|
||||
Ok(CountDrop)
|
||||
});
|
||||
});
|
||||
assert_eq!(DROP_COUNTER.load(Ordering::SeqCst), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_bytearray_to_array() {
|
||||
Python::with_gil(|py| {
|
||||
let v: [u8; 33] = py
|
||||
.eval(
|
||||
"bytearray(b'abcabcabcabcabcabcabcabcabcabcabc')",
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap()
|
||||
.extract()
|
||||
.unwrap();
|
||||
assert!(&v == b"abcabcabcabcabcabcabcabcabcabcabc");
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_small_bytearray_to_array() {
|
||||
Python::with_gil(|py| {
|
||||
|
@ -390,4 +242,16 @@ mod tests {
|
|||
let _cell: &crate::PyCell<Foo> = list.get_item(4).unwrap().extract().unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/59211505
|
||||
fn catch_unwind_silent<F, R>(f: F) -> std::thread::Result<R>
|
||||
where
|
||||
F: FnOnce() -> R + panic::UnwindSafe,
|
||||
{
|
||||
let prev_hook = panic::take_hook();
|
||||
panic::set_hook(Box::new(|_| {}));
|
||||
let result = panic::catch_unwind(f);
|
||||
panic::set_hook(prev_hook);
|
||||
result
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue