2911: add `Ellipsis()` and `is_ellipsis()` methods r=mejrs,davidhewitt a=samuelcolvin

fix #2906

Please consider adding the following to your pull request:
 - [x] an entry for this PR in newsfragments - see [https://pyo3.rs/main/contributing.html#documenting-changes]
 - [x] docs to all new functions and / or detail in the guide
 - [x] tests for all new or changed functions


Co-authored-by: Samuel Colvin <s@muelcolvin.com>
This commit is contained in:
bors[bot] 2023-01-27 06:41:54 +00:00 committed by GitHub
commit 794e19d796
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 62 additions and 0 deletions

View File

@ -0,0 +1 @@
Add `py.Ellipsis()` and `py_any.is_ellipsis()` methods.

View File

@ -2,6 +2,7 @@ use crate::object::*;
use crate::pyport::Py_ssize_t;
use std::os::raw::c_int;
#[cfg_attr(windows, link(name = "pythonXY"))]
extern "C" {
#[cfg_attr(PyPy, link_name = "_PyPy_EllipsisObject")]
static mut _Py_EllipsisObject: PyObject;

View File

@ -519,6 +519,13 @@ impl<T> Py<T> {
unsafe { ffi::Py_None() == self.as_ptr() }
}
/// Returns whether the object is Ellipsis, e.g. `...`.
///
/// This is equivalent to the Python expression `self is ...`.
pub fn is_ellipsis(&self) -> bool {
unsafe { ffi::Py_Ellipsis() == self.as_ptr() }
}
/// Returns whether the object is considered to be true.
///
/// This is equivalent to the Python expression `bool(self)`.
@ -1147,6 +1154,22 @@ a = A()
})
}
#[test]
fn test_is_ellipsis() {
Python::with_gil(|py| {
let v = py
.eval("...", None, None)
.map_err(|e| e.print(py))
.unwrap()
.to_object(py);
assert!(v.is_ellipsis());
let not_ellipsis = 5.to_object(py);
assert!(!not_ellipsis.is_ellipsis());
});
}
#[cfg(feature = "macros")]
mod using_macros {
use super::*;

View File

@ -619,6 +619,13 @@ impl<'py> Python<'py> {
unsafe { PyObject::from_borrowed_ptr(self, ffi::Py_None()) }
}
/// Gets the Python builtin value `Ellipsis`, or `...`.
#[allow(non_snake_case)] // the Python keyword starts with uppercase
#[inline]
pub fn Ellipsis(self) -> PyObject {
unsafe { PyObject::from_borrowed_ptr(self, ffi::Py_Ellipsis()) }
}
/// Gets the Python builtin value `NotImplemented`.
#[allow(non_snake_case)] // the Python keyword starts with uppercase
#[inline]
@ -1032,4 +1039,15 @@ mod tests {
let state = unsafe { crate::ffi::PyGILState_Check() };
assert_eq!(state, GIL_NOT_HELD);
}
#[test]
fn test_ellipsis() {
Python::with_gil(|py| {
assert_eq!(py.Ellipsis().to_string(), "Ellipsis");
let v = py.eval("...", None, None).map_err(|e| e.print(py)).unwrap();
assert!(v.eq(py.Ellipsis()).unwrap());
});
}
}

View File

@ -669,6 +669,13 @@ impl PyAny {
unsafe { ffi::Py_None() == self.as_ptr() }
}
/// Returns whether the object is Ellipsis, e.g. `...`.
///
/// This is equivalent to the Python expression `self is ...`.
pub fn is_ellipsis(&self) -> bool {
unsafe { ffi::Py_Ellipsis() == self.as_ptr() }
}
/// Returns true if the sequence or mapping has a length of 0.
///
/// This is equivalent to the Python expression `len(self) == 0`.
@ -1135,4 +1142,16 @@ class SimpleClass:
let bools = [true, false];
test_eq_methods_generic(&bools);
}
#[test]
fn test_is_ellipsis() {
Python::with_gil(|py| {
let v = py.eval("...", None, None).map_err(|e| e.print(py)).unwrap();
assert!(v.is_ellipsis());
let not_ellipsis = 5.to_object(py).into_ref(py);
assert!(!not_ellipsis.is_ellipsis());
});
}
}