Merge branch 'main' into PyNone-new-api
This commit is contained in:
commit
7efd412a63
|
@ -103,7 +103,7 @@ given signatures should be interpreted as follows:
|
||||||
match op {
|
match op {
|
||||||
CompareOp::Eq => (self.0 == other.0).into_py(py),
|
CompareOp::Eq => (self.0 == other.0).into_py(py),
|
||||||
CompareOp::Ne => (self.0 != other.0).into_py(py),
|
CompareOp::Ne => (self.0 != other.0).into_py(py),
|
||||||
_ => py.NotImplemented().into(),
|
_ => py.NotImplemented(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,7 @@ use pyo3::prelude::*;
|
||||||
use pyo3::types::{PyBool, PyList};
|
use pyo3::types::{PyBool, PyList};
|
||||||
|
|
||||||
Python::with_gil(|py| {
|
Python::with_gil(|py| {
|
||||||
assert!(PyBool::new(py, true).is_instance_of::<PyBool>());
|
assert!(PyBool::new_bound(py, true).is_instance_of::<PyBool>());
|
||||||
let list = PyList::new_bound(py, &[1, 2, 3, 4]);
|
let list = PyList::new_bound(py, &[1, 2, 3, 4]);
|
||||||
assert!(!list.is_instance_of::<PyBool>());
|
assert!(!list.is_instance_of::<PyBool>());
|
||||||
assert!(list.is_instance_of::<PyList>());
|
assert!(list.is_instance_of::<PyList>());
|
||||||
|
|
|
@ -82,39 +82,6 @@ Python::with_gil(|py| {
|
||||||
# }
|
# }
|
||||||
```
|
```
|
||||||
|
|
||||||
### `py.None()`, `py.NotImplemented()` and `py.Ellipsis()` now return typed singletons
|
|
||||||
|
|
||||||
Previously `py.None()`, `py.NotImplemented()` and `py.Ellipsis()` would return `PyObject`. This had a few downsides:
|
|
||||||
- `PyObject` does not carry static type information
|
|
||||||
- `PyObject` takes ownership of a reference to the singletons, adding refcounting performance overhead
|
|
||||||
- `PyObject` is not gil-bound, meaning follow up method calls might again need `py`, causing repetition
|
|
||||||
|
|
||||||
To avoid these downsides, these methods now return typed gil-bound references to the singletons, e.g. `py.None()` returns `&PyNone`. These typed singletons all implement `Into<PyObject>`, so migration is straightforward.
|
|
||||||
|
|
||||||
Before:
|
|
||||||
|
|
||||||
```rust,compile_fail
|
|
||||||
# use pyo3::prelude::*;
|
|
||||||
Python::with_gil(|py| {
|
|
||||||
let a: PyObject = py.None();
|
|
||||||
|
|
||||||
// let b: &PyAny = py.None().as_ref(py); // or into_ref(py)
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
After:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
# use pyo3::prelude::*;
|
|
||||||
Python::with_gil(|py| {
|
|
||||||
// For uses needing a PyObject, add `.into()`
|
|
||||||
let a: PyObject = py.None().into_py(py);
|
|
||||||
|
|
||||||
// For uses needing &PyAny, remove `.as_ref(py)`
|
|
||||||
// let b: &PyAny = py.None();
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### `Iter(A)NextOutput` are deprecated
|
### `Iter(A)NextOutput` are deprecated
|
||||||
|
|
||||||
The `__next__` and `__anext__` magic methods can now return any type convertible into Python objects directly just like all other `#[pymethods]`. The `IterNextOutput` used by `__next__` and `IterANextOutput` used by `__anext__` are subsequently deprecated. Most importantly, this change allows returning an awaitable from `__anext__` without non-sensically wrapping it into `Yield` or `Some`. Only the return types `Option<T>` and `Result<Option<T>, E>` are still handled in a special manner where `Some(val)` yields `val` and `None` stops iteration.
|
The `__next__` and `__anext__` magic methods can now return any type convertible into Python objects directly just like all other `#[pymethods]`. The `IterNextOutput` used by `__next__` and `IterANextOutput` used by `__anext__` are subsequently deprecated. Most importantly, this change allows returning an awaitable from `__anext__` without non-sensically wrapping it into `Yield` or `Some`. Only the return types `Option<T>` and `Result<Option<T>, E>` are still handled in a special manner where `Some(val)` yields `val` and `None` stops iteration.
|
||||||
|
@ -433,7 +400,7 @@ fn raise_err() -> anyhow::Result<()> {
|
||||||
Err(PyValueError::new_err("original error message").into())
|
Err(PyValueError::new_err("original error message").into())
|
||||||
}
|
}
|
||||||
|
|
||||||
# fn main() {
|
fn main() {
|
||||||
Python::with_gil(|py| {
|
Python::with_gil(|py| {
|
||||||
let rs_func = wrap_pyfunction!(raise_err, py).unwrap();
|
let rs_func = wrap_pyfunction!(raise_err, py).unwrap();
|
||||||
pyo3::py_run!(
|
pyo3::py_run!(
|
||||||
|
@ -1212,14 +1179,14 @@ ensure that the Python GIL was held by the current thread). Technically, this wa
|
||||||
To migrate, just pass a `py` argument to any calls to these methods.
|
To migrate, just pass a `py` argument to any calls to these methods.
|
||||||
|
|
||||||
Before:
|
Before:
|
||||||
```rust,ignore
|
```rust,compile_fail
|
||||||
# pyo3::Python::with_gil(|py| {
|
# pyo3::Python::with_gil(|py| {
|
||||||
py.None().get_refcnt();
|
py.None().get_refcnt();
|
||||||
# })
|
# })
|
||||||
```
|
```
|
||||||
|
|
||||||
After:
|
After:
|
||||||
```rust,compile_fail
|
```rust
|
||||||
# pyo3::Python::with_gil(|py| {
|
# pyo3::Python::with_gil(|py| {
|
||||||
py.None().get_refcnt(py);
|
py.None().get_refcnt(py);
|
||||||
# })
|
# })
|
||||||
|
|
|
@ -474,7 +474,7 @@ class House(object):
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let none = py.None();
|
let none = py.None();
|
||||||
house
|
house
|
||||||
.call_method1("__exit__", (none, none, none))
|
.call_method1("__exit__", (&none, &none, &none))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
Change return type of `py.None()`, `py.NotImplemented()`, and `py.Ellipsis()` from `PyObject` to typed singletons (`&PyNone`, `&PyNotImplemented` and `PyEllipsis` respectively).
|
|
|
@ -8,7 +8,7 @@ fn bench_clean_acquire_gil(b: &mut Bencher<'_>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bench_dirty_acquire_gil(b: &mut Bencher<'_>) {
|
fn bench_dirty_acquire_gil(b: &mut Bencher<'_>) {
|
||||||
let obj: PyObject = Python::with_gil(|py| py.None().into_py(py));
|
let obj = Python::with_gil(|py| py.None());
|
||||||
b.iter_batched(
|
b.iter_batched(
|
||||||
|| {
|
|| {
|
||||||
// Clone and drop an object so that the GILPool has work to do.
|
// Clone and drop an object so that the GILPool has work to do.
|
||||||
|
|
|
@ -6,7 +6,7 @@ fn drop_many_objects(b: &mut Bencher<'_>) {
|
||||||
Python::with_gil(|py| {
|
Python::with_gil(|py| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
for _ in 0..1000 {
|
for _ in 0..1000 {
|
||||||
std::mem::drop(PyObject::from(py.None()));
|
std::mem::drop(py.None());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -743,7 +743,7 @@ fn impl_simple_enum(
|
||||||
return Ok((self_val == other.__pyo3__int__()).to_object(py));
|
return Ok((self_val == other.__pyo3__int__()).to_object(py));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(::std::convert::Into::into(py.NotImplemented()));
|
return Ok(py.NotImplemented());
|
||||||
}
|
}
|
||||||
_pyo3::basic::CompareOp::Ne => {
|
_pyo3::basic::CompareOp::Ne => {
|
||||||
let self_val = self.__pyo3__int__();
|
let self_val = self.__pyo3__int__();
|
||||||
|
@ -754,9 +754,9 @@ fn impl_simple_enum(
|
||||||
return Ok((self_val != other.__pyo3__int__()).to_object(py));
|
return Ok((self_val != other.__pyo3__int__()).to_object(py));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(::std::convert::Into::into(py.NotImplemented()));
|
return Ok(py.NotImplemented());
|
||||||
}
|
}
|
||||||
_ => Ok(::std::convert::Into::into(py.NotImplemented())),
|
_ => Ok(py.NotImplemented()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -37,7 +37,7 @@ impl IterAwaitable {
|
||||||
Ok(v) => Err(PyStopIteration::new_err(v)),
|
Ok(v) => Err(PyStopIteration::new_err(v)),
|
||||||
Err(err) => Err(err),
|
Err(err) => Err(err),
|
||||||
},
|
},
|
||||||
_ => Ok(py.None().into_py(py)),
|
_ => Ok(py.None()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,7 +141,7 @@ pub trait ToPyObject {
|
||||||
/// match self {
|
/// match self {
|
||||||
/// Self::Integer(val) => val.into_py(py),
|
/// Self::Integer(val) => val.into_py(py),
|
||||||
/// Self::String(val) => val.into_py(py),
|
/// Self::String(val) => val.into_py(py),
|
||||||
/// Self::None => py.None().into_py(py),
|
/// Self::None => py.None(),
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
|
@ -266,7 +266,7 @@ where
|
||||||
{
|
{
|
||||||
fn to_object(&self, py: Python<'_>) -> PyObject {
|
fn to_object(&self, py: Python<'_>) -> PyObject {
|
||||||
self.as_ref()
|
self.as_ref()
|
||||||
.map_or_else(|| py.None().into_py(py), |val| val.to_object(py))
|
.map_or_else(|| py.None(), |val| val.to_object(py))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,7 +275,7 @@ where
|
||||||
T: IntoPy<PyObject>,
|
T: IntoPy<PyObject>,
|
||||||
{
|
{
|
||||||
fn into_py(self, py: Python<'_>) -> PyObject {
|
fn into_py(self, py: Python<'_>) -> PyObject {
|
||||||
self.map_or_else(|| py.None().into_py(py), |val| val.into_py(py))
|
self.map_or_else(|| py.None(), |val| val.into_py(py))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -593,7 +593,6 @@ mod test_no_clone {}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::conversion::IntoPy;
|
|
||||||
use crate::{PyObject, Python};
|
use crate::{PyObject, Python};
|
||||||
|
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
|
@ -648,13 +647,13 @@ mod tests {
|
||||||
assert_eq!(option.as_ptr(), std::ptr::null_mut());
|
assert_eq!(option.as_ptr(), std::ptr::null_mut());
|
||||||
|
|
||||||
let none = py.None();
|
let none = py.None();
|
||||||
option = Some(none.into_py(py));
|
option = Some(none.clone());
|
||||||
|
|
||||||
let ref_cnt = none.into_py(py).get_refcnt(py);
|
let ref_cnt = none.get_refcnt(py);
|
||||||
assert_eq!(option.as_ptr(), none.as_ptr());
|
assert_eq!(option.as_ptr(), none.as_ptr());
|
||||||
|
|
||||||
// Ensure ref count not changed by as_ptr call
|
// Ensure ref count not changed by as_ptr call
|
||||||
assert_eq!(none.into_py(py).get_refcnt(py), ref_cnt);
|
assert_eq!(none.get_refcnt(py), ref_cnt);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -566,7 +566,6 @@ fn timezone_utc(py: Python<'_>) -> &PyAny {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::types::any::PyAnyMethods;
|
|
||||||
use crate::{types::PyTuple, Py};
|
use crate::{types::PyTuple, Py};
|
||||||
use std::{cmp::Ordering, panic};
|
use std::{cmp::Ordering, panic};
|
||||||
|
|
||||||
|
@ -636,7 +635,7 @@ mod tests {
|
||||||
// Test that if a user tries to convert a python's timezone aware datetime into a naive
|
// Test that if a user tries to convert a python's timezone aware datetime into a naive
|
||||||
// one, the conversion fails.
|
// one, the conversion fails.
|
||||||
Python::with_gil(|py| {
|
Python::with_gil(|py| {
|
||||||
let none = py.None();
|
let none = py.None().into_ref(py);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
none.extract::<Duration>().unwrap_err().to_string(),
|
none.extract::<Duration>().unwrap_err().to_string(),
|
||||||
"TypeError: 'NoneType' object cannot be converted to 'PyDelta'"
|
"TypeError: 'NoneType' object cannot be converted to 'PyDelta'"
|
||||||
|
|
|
@ -219,7 +219,7 @@ impl PyErr {
|
||||||
} else {
|
} else {
|
||||||
// Assume obj is Type[Exception]; let later normalization handle if this
|
// Assume obj is Type[Exception]; let later normalization handle if this
|
||||||
// is not the case
|
// is not the case
|
||||||
PyErrState::lazy(obj, Option::<PyObject>::None)
|
PyErrState::lazy(obj, obj.py().None())
|
||||||
};
|
};
|
||||||
|
|
||||||
PyErr::from_state(state)
|
PyErr::from_state(state)
|
||||||
|
|
|
@ -311,20 +311,18 @@ fn test_inc_dec_ref() {
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(Py_3_12)]
|
#[cfg(Py_3_12)]
|
||||||
fn test_inc_dec_ref_immortal() {
|
fn test_inc_dec_ref_immortal() {
|
||||||
use crate::types::any::PyAnyMethods;
|
|
||||||
|
|
||||||
Python::with_gil(|py| {
|
Python::with_gil(|py| {
|
||||||
let obj = py.None();
|
let obj = py.None();
|
||||||
|
|
||||||
let ref_count = obj.get_refcnt();
|
let ref_count = obj.get_refcnt(py);
|
||||||
let ptr = obj.as_ptr();
|
let ptr = obj.as_ptr();
|
||||||
|
|
||||||
unsafe { Py_INCREF(ptr) };
|
unsafe { Py_INCREF(ptr) };
|
||||||
|
|
||||||
assert_eq!(obj.get_refcnt(), ref_count);
|
assert_eq!(obj.get_refcnt(py), ref_count);
|
||||||
|
|
||||||
unsafe { Py_DECREF(ptr) };
|
unsafe { Py_DECREF(ptr) };
|
||||||
|
|
||||||
assert_eq!(obj.get_refcnt(), ref_count);
|
assert_eq!(obj.get_refcnt(py), ref_count);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -813,7 +813,7 @@ slot_fragment_trait! {
|
||||||
// By default `__ne__` will try `__eq__` and invert the result
|
// By default `__ne__` will try `__eq__` and invert the result
|
||||||
let slf: &PyAny = py.from_borrowed_ptr(slf);
|
let slf: &PyAny = py.from_borrowed_ptr(slf);
|
||||||
let other: &PyAny = py.from_borrowed_ptr(other);
|
let other: &PyAny = py.from_borrowed_ptr(other);
|
||||||
slf.eq(other).map(|is_eq| PyBool::new(py, !is_eq).into_ptr())
|
slf.eq(other).map(|is_eq| PyBool::new_bound(py, !is_eq).to_owned().into_ptr())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -321,6 +321,14 @@ impl<'a, 'py> Borrowed<'a, 'py, PyAny> {
|
||||||
pub(crate) unsafe fn from_ptr_unchecked(py: Python<'py>, ptr: *mut ffi::PyObject) -> Self {
|
pub(crate) unsafe fn from_ptr_unchecked(py: Python<'py>, ptr: *mut ffi::PyObject) -> Self {
|
||||||
Self(NonNull::new_unchecked(ptr), PhantomData, py)
|
Self(NonNull::new_unchecked(ptr), PhantomData, py)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts this `PyAny` to a concrete Python type without checking validity.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// Callers must ensure that the type is valid or risk type confusion.
|
||||||
|
pub(crate) unsafe fn downcast_unchecked<T>(self) -> Borrowed<'a, 'py, T> {
|
||||||
|
Borrowed(self.0, PhantomData, self.2)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'py, T> From<&'a Bound<'py, T>> for Borrowed<'a, 'py, T> {
|
impl<'a, 'py, T> From<&'a Bound<'py, T>> for Borrowed<'a, 'py, T> {
|
||||||
|
|
|
@ -698,22 +698,22 @@ impl<'py> Python<'py> {
|
||||||
/// Gets the Python builtin value `None`.
|
/// Gets the Python builtin value `None`.
|
||||||
#[allow(non_snake_case)] // the Python keyword starts with uppercase
|
#[allow(non_snake_case)] // the Python keyword starts with uppercase
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn None(self) -> Borrowed<'py, 'py, PyNone> {
|
pub fn None(self) -> PyObject {
|
||||||
PyNone::get(self)
|
PyNone::get(self).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the Python builtin value `Ellipsis`, or `...`.
|
/// Gets the Python builtin value `Ellipsis`, or `...`.
|
||||||
#[allow(non_snake_case)] // the Python keyword starts with uppercase
|
#[allow(non_snake_case)] // the Python keyword starts with uppercase
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn Ellipsis(self) -> &'py PyEllipsis {
|
pub fn Ellipsis(self) -> PyObject {
|
||||||
PyEllipsis::get(self)
|
PyEllipsis::get(self).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the Python builtin value `NotImplemented`.
|
/// Gets the Python builtin value `NotImplemented`.
|
||||||
#[allow(non_snake_case)] // the Python keyword starts with uppercase
|
#[allow(non_snake_case)] // the Python keyword starts with uppercase
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn NotImplemented(self) -> &'py PyNotImplemented {
|
pub fn NotImplemented(self) -> PyObject {
|
||||||
PyNotImplemented::get(self)
|
PyNotImplemented::get(self).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the running Python interpreter version as a string.
|
/// Gets the running Python interpreter version as a string.
|
||||||
|
|
|
@ -759,9 +759,9 @@ impl PyAny {
|
||||||
/// use pyo3::types::{PyBool, PyLong};
|
/// use pyo3::types::{PyBool, PyLong};
|
||||||
///
|
///
|
||||||
/// Python::with_gil(|py| {
|
/// Python::with_gil(|py| {
|
||||||
/// let b = PyBool::new(py, true);
|
/// let b = PyBool::new_bound(py, true);
|
||||||
/// assert!(b.is_instance_of::<PyBool>());
|
/// assert!(b.is_instance_of::<PyBool>());
|
||||||
/// let any: &PyAny = b.as_ref();
|
/// let any: &Bound<'_, PyAny> = b.as_any();
|
||||||
///
|
///
|
||||||
/// // `bool` is a subtype of `int`, so `downcast` will accept a `bool` as an `int`
|
/// // `bool` is a subtype of `int`, so `downcast` will accept a `bool` as an `int`
|
||||||
/// // but `downcast_exact` will not.
|
/// // but `downcast_exact` will not.
|
||||||
|
@ -1583,9 +1583,9 @@ pub trait PyAnyMethods<'py> {
|
||||||
/// use pyo3::types::{PyBool, PyLong};
|
/// use pyo3::types::{PyBool, PyLong};
|
||||||
///
|
///
|
||||||
/// Python::with_gil(|py| {
|
/// Python::with_gil(|py| {
|
||||||
/// let b = PyBool::new(py, true);
|
/// let b = PyBool::new_bound(py, true);
|
||||||
/// assert!(b.is_instance_of::<PyBool>());
|
/// assert!(b.is_instance_of::<PyBool>());
|
||||||
/// let any: &PyAny = b.as_ref();
|
/// let any: &Bound<'_, PyAny> = b.as_any();
|
||||||
///
|
///
|
||||||
/// // `bool` is a subtype of `int`, so `downcast` will accept a `bool` as an `int`
|
/// // `bool` is a subtype of `int`, so `downcast` will accept a `bool` as an `int`
|
||||||
/// // but `downcast_exact` will not.
|
/// // but `downcast_exact` will not.
|
||||||
|
@ -2530,7 +2530,7 @@ class SimpleClass:
|
||||||
let x = 5.to_object(py).into_ref(py);
|
let x = 5.to_object(py).into_ref(py);
|
||||||
assert!(x.is_exact_instance_of::<PyLong>());
|
assert!(x.is_exact_instance_of::<PyLong>());
|
||||||
|
|
||||||
let t = PyBool::new(py, true);
|
let t = PyBool::new_bound(py, true);
|
||||||
assert!(t.is_instance_of::<PyLong>());
|
assert!(t.is_instance_of::<PyLong>());
|
||||||
assert!(!t.is_exact_instance_of::<PyLong>());
|
assert!(!t.is_exact_instance_of::<PyLong>());
|
||||||
assert!(t.is_exact_instance_of::<PyBool>());
|
assert!(t.is_exact_instance_of::<PyBool>());
|
||||||
|
@ -2543,10 +2543,12 @@ class SimpleClass:
|
||||||
#[test]
|
#[test]
|
||||||
fn test_any_is_exact_instance() {
|
fn test_any_is_exact_instance() {
|
||||||
Python::with_gil(|py| {
|
Python::with_gil(|py| {
|
||||||
let t = PyBool::new(py, true);
|
let t = PyBool::new_bound(py, true);
|
||||||
assert!(t.is_instance(py.get_type::<PyLong>()).unwrap());
|
assert!(t
|
||||||
assert!(!t.is_exact_instance(py.get_type::<PyLong>()));
|
.is_instance(&py.get_type::<PyLong>().as_borrowed())
|
||||||
assert!(t.is_exact_instance(py.get_type::<PyBool>()));
|
.unwrap());
|
||||||
|
assert!(!t.is_exact_instance(&py.get_type::<PyLong>().as_borrowed()));
|
||||||
|
assert!(t.is_exact_instance(&py.get_type::<PyBool>().as_borrowed()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#[cfg(feature = "experimental-inspect")]
|
#[cfg(feature = "experimental-inspect")]
|
||||||
use crate::inspect::types::TypeInfo;
|
use crate::inspect::types::TypeInfo;
|
||||||
use crate::{
|
use crate::{
|
||||||
exceptions::PyTypeError, ffi, instance::Bound, FromPyObject, IntoPy, PyAny, PyNativeType,
|
exceptions::PyTypeError, ffi, ffi_ptr_ext::FfiPtrExt, instance::Bound, Borrowed, FromPyObject,
|
||||||
PyObject, PyResult, Python, ToPyObject,
|
IntoPy, PyAny, PyNativeType, PyObject, PyResult, Python, ToPyObject,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::any::PyAnyMethods;
|
use super::any::PyAnyMethods;
|
||||||
|
@ -14,12 +14,33 @@ pub struct PyBool(PyAny);
|
||||||
pyobject_native_type!(PyBool, ffi::PyObject, pyobject_native_static_type_object!(ffi::PyBool_Type), #checkfunction=ffi::PyBool_Check);
|
pyobject_native_type!(PyBool, ffi::PyObject, pyobject_native_static_type_object!(ffi::PyBool_Type), #checkfunction=ffi::PyBool_Check);
|
||||||
|
|
||||||
impl PyBool {
|
impl PyBool {
|
||||||
/// Depending on `val`, returns `true` or `false`.
|
/// Deprecated form of [`PyBool::new_bound`]
|
||||||
|
#[cfg_attr(
|
||||||
|
not(feature = "gil-refs"),
|
||||||
|
deprecated(
|
||||||
|
since = "0.21.0",
|
||||||
|
note = "`PyBool::new` will be replaced by `PyBool::new_bound` in a future PyO3 version"
|
||||||
|
)
|
||||||
|
)]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(py: Python<'_>, val: bool) -> &PyBool {
|
pub fn new(py: Python<'_>, val: bool) -> &PyBool {
|
||||||
unsafe { py.from_borrowed_ptr(if val { ffi::Py_True() } else { ffi::Py_False() }) }
|
unsafe { py.from_borrowed_ptr(if val { ffi::Py_True() } else { ffi::Py_False() }) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Depending on `val`, returns `true` or `false`.
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
/// This returns a [`Borrowed`] reference to one of Pythons `True` or
|
||||||
|
/// `False` singletons
|
||||||
|
#[inline]
|
||||||
|
pub fn new_bound(py: Python<'_>, val: bool) -> Borrowed<'_, '_, Self> {
|
||||||
|
unsafe {
|
||||||
|
if val { ffi::Py_True() } else { ffi::Py_False() }
|
||||||
|
.assume_borrowed(py)
|
||||||
|
.downcast_unchecked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets whether this boolean is `true`.
|
/// Gets whether this boolean is `true`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_true(&self) -> bool {
|
pub fn is_true(&self) -> bool {
|
||||||
|
@ -65,7 +86,7 @@ impl ToPyObject for bool {
|
||||||
impl IntoPy<PyObject> for bool {
|
impl IntoPy<PyObject> for bool {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into_py(self, py: Python<'_>) -> PyObject {
|
fn into_py(self, py: Python<'_>) -> PyObject {
|
||||||
PyBool::new(py, self).into()
|
PyBool::new_bound(py, self).into_py(py)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "experimental-inspect")]
|
#[cfg(feature = "experimental-inspect")]
|
||||||
|
@ -135,27 +156,29 @@ impl FromPyObject<'_> for bool {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::types::{PyAny, PyBool};
|
use crate::types::any::PyAnyMethods;
|
||||||
|
use crate::types::boolobject::PyBoolMethods;
|
||||||
|
use crate::types::PyBool;
|
||||||
use crate::Python;
|
use crate::Python;
|
||||||
use crate::ToPyObject;
|
use crate::ToPyObject;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_true() {
|
fn test_true() {
|
||||||
Python::with_gil(|py| {
|
Python::with_gil(|py| {
|
||||||
assert!(PyBool::new(py, true).is_true());
|
assert!(PyBool::new_bound(py, true).is_true());
|
||||||
let t: &PyAny = PyBool::new(py, true).into();
|
let t = PyBool::new_bound(py, true);
|
||||||
assert!(t.extract::<bool>().unwrap());
|
assert!(t.as_any().extract::<bool>().unwrap());
|
||||||
assert!(true.to_object(py).is(PyBool::new(py, true)));
|
assert!(true.to_object(py).is(&*PyBool::new_bound(py, true)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_false() {
|
fn test_false() {
|
||||||
Python::with_gil(|py| {
|
Python::with_gil(|py| {
|
||||||
assert!(!PyBool::new(py, false).is_true());
|
assert!(!PyBool::new_bound(py, false).is_true());
|
||||||
let t: &PyAny = PyBool::new(py, false).into();
|
let t = PyBool::new_bound(py, false);
|
||||||
assert!(!t.extract::<bool>().unwrap());
|
assert!(!t.as_any().extract::<bool>().unwrap());
|
||||||
assert!(false.to_object(py).is(PyBool::new(py, false)));
|
assert!(false.to_object(py).is(&*PyBool::new_bound(py, false)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -596,7 +596,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_from_err() {
|
fn test_from_err() {
|
||||||
Python::with_gil(|py| {
|
Python::with_gil(|py| {
|
||||||
if let Err(err) = PyByteArray::from_bound(&py.None().as_borrowed()) {
|
if let Err(err) = PyByteArray::from_bound(py.None().bind(py)) {
|
||||||
assert!(err.is_instance_of::<exceptions::PyTypeError>(py));
|
assert!(err.is_instance_of::<exceptions::PyTypeError>(py));
|
||||||
} else {
|
} else {
|
||||||
panic!("error");
|
panic!("error");
|
||||||
|
|
|
@ -486,7 +486,7 @@ impl RichComparisons2 {
|
||||||
match op {
|
match op {
|
||||||
CompareOp::Eq => true.into_py(other.py()),
|
CompareOp::Eq => true.into_py(other.py()),
|
||||||
CompareOp::Ne => false.into_py(other.py()),
|
CompareOp::Ne => false.into_py(other.py()),
|
||||||
_ => other.py().NotImplemented().into(),
|
_ => other.py().NotImplemented(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -556,7 +556,7 @@ mod return_not_implemented {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn __richcmp__(&self, other: PyRef<'_, Self>, _op: CompareOp) -> PyObject {
|
fn __richcmp__(&self, other: PyRef<'_, Self>, _op: CompareOp) -> PyObject {
|
||||||
other.py().None().into_py(other.py())
|
other.py().None()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn __add__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> {
|
fn __add__<'p>(slf: PyRef<'p, Self>, _other: PyRef<'p, Self>) -> PyRef<'p, Self> {
|
||||||
|
|
|
@ -190,12 +190,12 @@ fn test_renaming_all_struct_fields() {
|
||||||
let struct_class = py.get_type::<StructWithRenamedFields>();
|
let struct_class = py.get_type::<StructWithRenamedFields>();
|
||||||
let struct_obj = struct_class.call0().unwrap();
|
let struct_obj = struct_class.call0().unwrap();
|
||||||
assert!(struct_obj
|
assert!(struct_obj
|
||||||
.setattr("firstField", PyBool::new(py, false))
|
.setattr("firstField", PyBool::new_bound(py, false))
|
||||||
.is_ok());
|
.is_ok());
|
||||||
py_assert!(py, struct_obj, "struct_obj.firstField == False");
|
py_assert!(py, struct_obj, "struct_obj.firstField == False");
|
||||||
py_assert!(py, struct_obj, "struct_obj.secondField == 5");
|
py_assert!(py, struct_obj, "struct_obj.secondField == 5");
|
||||||
assert!(struct_obj
|
assert!(struct_obj
|
||||||
.setattr("third_field", PyBool::new(py, true))
|
.setattr("third_field", PyBool::new_bound(py, true))
|
||||||
.is_ok());
|
.is_ok());
|
||||||
py_assert!(py, struct_obj, "struct_obj.third_field == True");
|
py_assert!(py, struct_obj, "struct_obj.third_field == True");
|
||||||
});
|
});
|
||||||
|
|
|
@ -119,7 +119,7 @@ fn test_polymorphic_container_does_not_accept_other_types() {
|
||||||
let setattr = |value: PyObject| p.as_ref(py).setattr("inner", value);
|
let setattr = |value: PyObject| p.as_ref(py).setattr("inner", value);
|
||||||
|
|
||||||
assert!(setattr(1i32.into_py(py)).is_err());
|
assert!(setattr(1i32.into_py(py)).is_err());
|
||||||
assert!(setattr(py.None().into_py(py)).is_err());
|
assert!(setattr(py.None()).is_err());
|
||||||
assert!(setattr((1i32, 2i32).into_py(py)).is_err());
|
assert!(setattr((1i32, 2i32).into_py(py)).is_err());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,7 +118,7 @@ fn test_write_unraisable() {
|
||||||
assert!(object.is_none(py));
|
assert!(object.is_none(py));
|
||||||
|
|
||||||
let err = PyRuntimeError::new_err("bar");
|
let err = PyRuntimeError::new_err("bar");
|
||||||
err.write_unraisable(py, Some(py.NotImplemented()));
|
err.write_unraisable(py, Some(py.NotImplemented().as_ref(py)));
|
||||||
|
|
||||||
let (err, object) = capture.borrow_mut(py).capture.take().unwrap();
|
let (err, object) = capture.borrow_mut(py).capture.take().unwrap();
|
||||||
assert_eq!(err.to_string(), "RuntimeError: bar");
|
assert_eq!(err.to_string(), "RuntimeError: bar");
|
||||||
|
|
|
@ -352,7 +352,7 @@ fn test_enum() {
|
||||||
_ => panic!("Expected extracting Foo::TransparentTuple, got {:?}", f),
|
_ => panic!("Expected extracting Foo::TransparentTuple, got {:?}", f),
|
||||||
}
|
}
|
||||||
let none = py.None();
|
let none = py.None();
|
||||||
let f = Foo::extract_bound(none.as_ref()).expect("Failed to extract Foo from int");
|
let f = Foo::extract(none.as_ref(py)).expect("Failed to extract Foo from int");
|
||||||
match f {
|
match f {
|
||||||
Foo::TransparentStructVar { a } => assert!(a.is_none()),
|
Foo::TransparentStructVar { a } => assert!(a.is_none()),
|
||||||
_ => panic!("Expected extracting Foo::TransparentStructVar, got {:?}", f),
|
_ => panic!("Expected extracting Foo::TransparentStructVar, got {:?}", f),
|
||||||
|
|
|
@ -89,7 +89,7 @@ impl GcIntegration {
|
||||||
|
|
||||||
fn __clear__(&mut self) {
|
fn __clear__(&mut self) {
|
||||||
Python::with_gil(|py| {
|
Python::with_gil(|py| {
|
||||||
self.self_ref = py.None().into_py(py);
|
self.self_ref = py.None();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,7 @@ fn gc_integration() {
|
||||||
let inst = PyCell::new(
|
let inst = PyCell::new(
|
||||||
py,
|
py,
|
||||||
GcIntegration {
|
GcIntegration {
|
||||||
self_ref: py.None().into_py(py),
|
self_ref: py.None(),
|
||||||
dropped: TestDropCall {
|
dropped: TestDropCall {
|
||||||
drop_called: Arc::clone(&drop_called),
|
drop_called: Arc::clone(&drop_called),
|
||||||
},
|
},
|
||||||
|
@ -286,9 +286,7 @@ struct PartialTraverse {
|
||||||
|
|
||||||
impl PartialTraverse {
|
impl PartialTraverse {
|
||||||
fn new(py: Python<'_>) -> Self {
|
fn new(py: Python<'_>) -> Self {
|
||||||
Self {
|
Self { member: py.None() }
|
||||||
member: py.None().into_py(py),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,9 +322,7 @@ struct PanickyTraverse {
|
||||||
|
|
||||||
impl PanickyTraverse {
|
impl PanickyTraverse {
|
||||||
fn new(py: Python<'_>) -> Self {
|
fn new(py: Python<'_>) -> Self {
|
||||||
Self {
|
Self { member: py.None() }
|
||||||
member: py.None().into_py(py),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,10 @@
|
||||||
|
|
||||||
#![cfg(feature = "macros")]
|
#![cfg(feature = "macros")]
|
||||||
|
|
||||||
use pyo3::IntoPy;
|
|
||||||
|
|
||||||
#[pyo3::pyfunction]
|
#[pyo3::pyfunction]
|
||||||
#[pyo3(name = "identity", signature = (x = None))]
|
#[pyo3(name = "identity", signature = (x = None))]
|
||||||
fn basic_function(py: pyo3::Python<'_>, x: Option<pyo3::PyObject>) -> pyo3::PyObject {
|
fn basic_function(py: pyo3::Python<'_>, x: Option<pyo3::PyObject>) -> pyo3::PyObject {
|
||||||
x.unwrap_or_else(|| py.None().into_py(py))
|
x.unwrap_or_else(|| py.None())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyo3::pymodule]
|
#[pyo3::pymodule]
|
||||||
|
|
Loading…
Reference in New Issue