Allow more methods to take interned arguments (#2312)
* Allow more methods to take interned arguments * Changelog * Unify name bounds * Resolve merge conflict * reduce use of py_decref * Add some attr tests * Update migration
This commit is contained in:
parent
97db563253
commit
dce4377eb4
|
@ -14,10 +14,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
### Changed
|
||||
|
||||
- Several methods of `Py` and `PyAny` now accept `impl IntoPy<Py<PyString>>` rather than just `&str` to allow use of the `intern!` macro. [#2312](https://github.com/PyO3/pyo3/pull/2312)
|
||||
- Move `PyTypeObject::type_object` method to `PyTypeInfo` trait, and deprecate `PyTypeObject` trait. [#2287](https://github.com/PyO3/pyo3/pull/2287)
|
||||
- The deprecated `pyproto` feature is now disabled by default. [#2322](https://github.com/PyO3/pyo3/pull/2322)
|
||||
- Deprecate `ToBorrowedObject` trait (it is only used as a wrapper for `ToPyObject`). [#2333](https://github.com/PyO3/pyo3/pull/2333)
|
||||
|
||||
|
||||
## [0.16.4] - 2022-04-14
|
||||
|
||||
### Added
|
||||
|
|
|
@ -5,6 +5,35 @@ For a detailed list of all changes, see the [CHANGELOG](changelog.md).
|
|||
|
||||
## from 0.16.* to 0.17
|
||||
|
||||
|
||||
### Added `impl IntoPy<Py<PyString>> for &str`
|
||||
|
||||
This may cause inference errors.
|
||||
|
||||
Before:
|
||||
```rust,compile_fail
|
||||
# use pyo3::prelude::*;
|
||||
#
|
||||
# fn main() {
|
||||
Python::with_gil(|py| {
|
||||
// Cannot infer either `Py<PyAny>` or `Py<PyString>`
|
||||
let _test = "test".into_py(py);
|
||||
});
|
||||
# }
|
||||
```
|
||||
|
||||
After, some type annotations may be necessary:
|
||||
|
||||
```rust
|
||||
# use pyo3::prelude::*;
|
||||
#
|
||||
# fn main() {
|
||||
Python::with_gil(|py| {
|
||||
let _test: Py<PyAny> = "test".into_py(py);
|
||||
});
|
||||
# }
|
||||
```
|
||||
|
||||
### The `pyproto` feature is now disabled by default
|
||||
|
||||
In preparation for removing the deprecated `#[pyproto]` attribute macro in a future PyO3 version, it is now gated behind an opt-in feature flag. This also gives a slight saving to compile times for code which does not use the deprecated macro.
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::intern;
|
||||
use crate::types::PyType;
|
||||
use crate::{FromPyObject, IntoPy, PyAny, PyObject, PyResult, Python, ToPyObject};
|
||||
use std::borrow::Cow;
|
||||
|
@ -18,10 +19,10 @@ impl FromPyObject<'_> for PathBuf {
|
|||
Ok(s) => s,
|
||||
Err(err) => {
|
||||
let py = ob.py();
|
||||
let pathlib = py.import("pathlib")?;
|
||||
let pathlib_path: &PyType = pathlib.getattr("Path")?.downcast()?;
|
||||
let pathlib = py.import(intern!(py, "pathlib"))?;
|
||||
let pathlib_path: &PyType = pathlib.getattr(intern!(py, "Path"))?.downcast()?;
|
||||
if ob.is_instance(pathlib_path)? {
|
||||
let path_str = ob.call_method0("__str__")?;
|
||||
let path_str = ob.call_method0(intern!(py, "__str__"))?;
|
||||
OsString::extract(path_str)?
|
||||
} else {
|
||||
return Err(err);
|
||||
|
|
|
@ -135,7 +135,10 @@ impl PyErr {
|
|||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// use pyo3::{exceptions::PyTypeError, types::PyType, IntoPy, PyErr, Python};
|
||||
/// use pyo3::prelude::*;
|
||||
/// use pyo3::exceptions::PyTypeError;
|
||||
/// use pyo3::types::{PyType, PyString};
|
||||
///
|
||||
/// Python::with_gil(|py| {
|
||||
/// // Case #1: Exception object
|
||||
/// let err = PyErr::from_value(PyTypeError::new_err("some type error").value(py));
|
||||
|
@ -146,7 +149,7 @@ impl PyErr {
|
|||
/// assert_eq!(err.to_string(), "TypeError: ");
|
||||
///
|
||||
/// // Case #3: Invalid exception value
|
||||
/// let err = PyErr::from_value("foo".into_py(py).as_ref(py));
|
||||
/// let err = PyErr::from_value(PyString::new(py, "foo").into());
|
||||
/// assert_eq!(
|
||||
/// err.to_string(),
|
||||
/// "TypeError: exceptions must derive from BaseException"
|
||||
|
|
163
src/instance.rs
163
src/instance.rs
|
@ -3,7 +3,7 @@ use crate::conversion::PyTryFrom;
|
|||
use crate::err::{self, PyDowncastError, PyErr, PyResult};
|
||||
use crate::gil;
|
||||
use crate::pycell::{PyBorrowError, PyBorrowMutError, PyCell};
|
||||
use crate::types::{PyDict, PyTuple};
|
||||
use crate::types::{PyDict, PyString, PyTuple};
|
||||
use crate::{
|
||||
ffi, pyclass::MutablePyClass, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyAny, PyClass,
|
||||
PyClassInitializer, PyRef, PyRefMut, PyTypeInfo, Python, ToPyObject,
|
||||
|
@ -561,12 +561,14 @@ impl<T> Py<T> {
|
|||
/// ```
|
||||
pub fn getattr<N>(&self, py: Python<'_>, attr_name: N) -> PyResult<PyObject>
|
||||
where
|
||||
N: ToPyObject,
|
||||
N: IntoPy<Py<PyString>>,
|
||||
{
|
||||
let attr_name = attr_name.into_py(py);
|
||||
|
||||
unsafe {
|
||||
PyObject::from_owned_ptr_or_err(
|
||||
py,
|
||||
ffi::PyObject_GetAttr(self.as_ptr(), attr_name.to_object(py).as_ptr()),
|
||||
ffi::PyObject_GetAttr(self.as_ptr(), attr_name.as_ptr()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -575,8 +577,8 @@ impl<T> Py<T> {
|
|||
///
|
||||
/// This is equivalent to the Python expression `self.attr_name = value`.
|
||||
///
|
||||
/// If calling this method becomes performance-critical, the [`intern!`] macro can be used
|
||||
/// to intern `attr_name`, thereby avoiding repeated temporary allocations of Python strings.
|
||||
/// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used
|
||||
/// to intern `attr_name`.
|
||||
///
|
||||
/// # Example: `intern!`ing the attribute name
|
||||
///
|
||||
|
@ -595,17 +597,16 @@ impl<T> Py<T> {
|
|||
/// ```
|
||||
pub fn setattr<N, V>(&self, py: Python<'_>, attr_name: N, value: V) -> PyResult<()>
|
||||
where
|
||||
N: ToPyObject,
|
||||
V: ToPyObject,
|
||||
N: IntoPy<Py<PyString>>,
|
||||
V: IntoPy<Py<PyAny>>,
|
||||
{
|
||||
let attr_name = attr_name.into_py(py);
|
||||
let value = value.into_py(py);
|
||||
|
||||
unsafe {
|
||||
err::error_on_minusone(
|
||||
py,
|
||||
ffi::PyObject_SetAttr(
|
||||
self.as_ptr(),
|
||||
attr_name.to_object(py).as_ptr(),
|
||||
value.to_object(py).as_ptr(),
|
||||
),
|
||||
ffi::PyObject_SetAttr(self.as_ptr(), attr_name.as_ptr(), value.as_ptr()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -619,16 +620,17 @@ impl<T> Py<T> {
|
|||
args: impl IntoPy<Py<PyTuple>>,
|
||||
kwargs: Option<&PyDict>,
|
||||
) -> PyResult<PyObject> {
|
||||
let args = args.into_py(py).into_ptr();
|
||||
let args = args.into_py(py);
|
||||
let kwargs = kwargs.into_ptr();
|
||||
let result = unsafe {
|
||||
PyObject::from_owned_ptr_or_err(py, ffi::PyObject_Call(self.as_ptr(), args, kwargs))
|
||||
};
|
||||
|
||||
unsafe {
|
||||
ffi::Py_XDECREF(args);
|
||||
let ret = PyObject::from_owned_ptr_or_err(
|
||||
py,
|
||||
ffi::PyObject_Call(self.as_ptr(), args.as_ptr(), kwargs),
|
||||
);
|
||||
ffi::Py_XDECREF(kwargs);
|
||||
ret
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Calls the object with only positional arguments.
|
||||
|
@ -657,23 +659,29 @@ impl<T> Py<T> {
|
|||
/// Calls a method on the object.
|
||||
///
|
||||
/// This is equivalent to the Python expression `self.name(*args, **kwargs)`.
|
||||
pub fn call_method(
|
||||
///
|
||||
/// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used
|
||||
/// to intern `name`.
|
||||
pub fn call_method<N, A>(
|
||||
&self,
|
||||
py: Python<'_>,
|
||||
name: &str,
|
||||
args: impl IntoPy<Py<PyTuple>>,
|
||||
name: N,
|
||||
args: A,
|
||||
kwargs: Option<&PyDict>,
|
||||
) -> PyResult<PyObject> {
|
||||
unsafe {
|
||||
let args = args.into_py(py).into_ptr();
|
||||
) -> PyResult<PyObject>
|
||||
where
|
||||
N: IntoPy<Py<PyString>>,
|
||||
A: IntoPy<Py<PyTuple>>,
|
||||
{
|
||||
let callee = self.getattr(py, name)?;
|
||||
let args: Py<PyTuple> = args.into_py(py);
|
||||
let kwargs = kwargs.into_ptr();
|
||||
let ptr = ffi::PyObject_GetAttr(self.as_ptr(), name.to_object(py).as_ptr());
|
||||
if ptr.is_null() {
|
||||
return Err(PyErr::fetch(py));
|
||||
}
|
||||
let result = PyObject::from_owned_ptr_or_err(py, ffi::PyObject_Call(ptr, args, kwargs));
|
||||
ffi::Py_DECREF(ptr);
|
||||
ffi::Py_XDECREF(args);
|
||||
|
||||
unsafe {
|
||||
let result = PyObject::from_owned_ptr_or_err(
|
||||
py,
|
||||
ffi::PyObject_Call(callee.as_ptr(), args.as_ptr(), kwargs),
|
||||
);
|
||||
ffi::Py_XDECREF(kwargs);
|
||||
result
|
||||
}
|
||||
|
@ -682,24 +690,32 @@ impl<T> Py<T> {
|
|||
/// Calls a method on the object with only positional arguments.
|
||||
///
|
||||
/// This is equivalent to the Python expression `self.name(*args)`.
|
||||
pub fn call_method1(
|
||||
&self,
|
||||
py: Python<'_>,
|
||||
name: &str,
|
||||
args: impl IntoPy<Py<PyTuple>>,
|
||||
) -> PyResult<PyObject> {
|
||||
///
|
||||
/// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used
|
||||
/// to intern `name`.
|
||||
pub fn call_method1<N, A>(&self, py: Python<'_>, name: N, args: A) -> PyResult<PyObject>
|
||||
where
|
||||
N: IntoPy<Py<PyString>>,
|
||||
A: IntoPy<Py<PyTuple>>,
|
||||
{
|
||||
self.call_method(py, name, args, None)
|
||||
}
|
||||
|
||||
/// Calls a method on the object with no arguments.
|
||||
///
|
||||
/// This is equivalent to the Python expression `self.name()`.
|
||||
pub fn call_method0(&self, py: Python<'_>, name: &str) -> PyResult<PyObject> {
|
||||
///
|
||||
/// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used
|
||||
/// to intern `name`.
|
||||
pub fn call_method0<N>(&self, py: Python<'_>, name: N) -> PyResult<PyObject>
|
||||
where
|
||||
N: IntoPy<Py<PyString>>,
|
||||
{
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(Py_3_9, not(any(Py_LIMITED_API, PyPy))))] {
|
||||
// Optimized path on python 3.9+
|
||||
unsafe {
|
||||
let name = name.into_py(py);
|
||||
let name: Py<PyString> = name.into_py(py);
|
||||
PyObject::from_owned_ptr_or_err(py, ffi::PyObject_CallMethodNoArgs(self.as_ptr(), name.as_ptr()))
|
||||
}
|
||||
} else {
|
||||
|
@ -728,7 +744,7 @@ impl<T> Py<T> {
|
|||
|
||||
/// Create a `Py<T>` instance by taking ownership of the given FFI pointer.
|
||||
///
|
||||
/// If `ptr` is null then the current Python exception is fetched as a `PyErr`.
|
||||
/// If `ptr` is null then the current Python exception is fetched as a [`PyErr`].
|
||||
///
|
||||
/// # Safety
|
||||
/// If non-null, `ptr` must be a pointer to a Python object of type T.
|
||||
|
@ -985,8 +1001,8 @@ impl PyObject {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Py, PyObject};
|
||||
use crate::types::PyDict;
|
||||
use crate::{Python, ToPyObject};
|
||||
use crate::types::{PyDict, PyString};
|
||||
use crate::{PyAny, PyResult, Python, ToPyObject};
|
||||
|
||||
#[test]
|
||||
fn test_call0() {
|
||||
|
@ -1036,4 +1052,67 @@ mod tests {
|
|||
assert_eq!(p.get_refcnt(py), cnt);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn attr() -> PyResult<()> {
|
||||
use crate::types::PyModule;
|
||||
|
||||
Python::with_gil(|py| {
|
||||
const CODE: &str = r#"
|
||||
class A:
|
||||
pass
|
||||
a = A()
|
||||
"#;
|
||||
let module = PyModule::from_code(py, CODE, "", "")?;
|
||||
let instance: Py<PyAny> = module.getattr("a")?.into();
|
||||
|
||||
instance.getattr(py, "foo").unwrap_err();
|
||||
|
||||
instance.setattr(py, "foo", "bar")?;
|
||||
|
||||
assert!(instance
|
||||
.getattr(py, "foo")?
|
||||
.as_ref(py)
|
||||
.eq(PyString::new(py, "bar"))?);
|
||||
|
||||
instance.getattr(py, "foo")?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pystring_attr() -> PyResult<()> {
|
||||
use crate::types::PyModule;
|
||||
|
||||
Python::with_gil(|py| {
|
||||
const CODE: &str = r#"
|
||||
class A:
|
||||
pass
|
||||
a = A()
|
||||
"#;
|
||||
let module = PyModule::from_code(py, CODE, "", "")?;
|
||||
let instance: Py<PyAny> = module.getattr("a")?.into();
|
||||
|
||||
let foo = crate::intern!(py, "foo");
|
||||
let bar = crate::intern!(py, "bar");
|
||||
|
||||
instance.getattr(py, foo).unwrap_err();
|
||||
instance.setattr(py, foo, bar)?;
|
||||
assert!(instance.getattr(py, foo)?.as_ref(py).eq(bar)?);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_attr() -> PyResult<()> {
|
||||
Python::with_gil(|py| {
|
||||
let instance: Py<PyAny> = py.eval("object()", None, None)?.into();
|
||||
|
||||
instance.getattr(py, "foo").unwrap_err();
|
||||
|
||||
// Cannot assign arbitrary attributes to `object`
|
||||
instance.setattr(py, "foo", "bar").unwrap_err();
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -122,10 +122,11 @@
|
|||
use crate::err::{self, PyDowncastError, PyErr, PyResult};
|
||||
use crate::gil::{self, GILGuard, GILPool};
|
||||
use crate::impl_::not_send::NotSend;
|
||||
use crate::types::{PyAny, PyDict, PyModule, PyType};
|
||||
use crate::types::{PyAny, PyDict, PyModule, PyString, PyType};
|
||||
use crate::version::PythonVersionInfo;
|
||||
use crate::{
|
||||
ffi, AsPyPointer, FromPyPointer, IntoPyPointer, PyNativeType, PyObject, PyTryFrom, PyTypeInfo,
|
||||
ffi, AsPyPointer, FromPyPointer, IntoPy, IntoPyPointer, Py, PyNativeType, PyObject, PyTryFrom,
|
||||
PyTypeInfo,
|
||||
};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::marker::PhantomData;
|
||||
|
@ -590,7 +591,10 @@ impl<'py> Python<'py> {
|
|||
}
|
||||
|
||||
/// Imports the Python module with the specified name.
|
||||
pub fn import(self, name: &str) -> PyResult<&'py PyModule> {
|
||||
pub fn import<N>(self, name: N) -> PyResult<&'py PyModule>
|
||||
where
|
||||
N: IntoPy<Py<PyString>>,
|
||||
{
|
||||
PyModule::import(self, name)
|
||||
}
|
||||
|
||||
|
|
304
src/types/any.rs
304
src/types/any.rs
|
@ -97,21 +97,25 @@ impl PyAny {
|
|||
/// Determines whether this object has the given attribute.
|
||||
///
|
||||
/// This is equivalent to the Python expression `hasattr(self, attr_name)`.
|
||||
///
|
||||
/// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used
|
||||
/// to intern `attr_name`.
|
||||
pub fn hasattr<N>(&self, attr_name: N) -> PyResult<bool>
|
||||
where
|
||||
N: ToPyObject,
|
||||
N: IntoPy<Py<PyString>>,
|
||||
{
|
||||
unsafe {
|
||||
Ok(ffi::PyObject_HasAttr(self.as_ptr(), attr_name.to_object(self.py()).as_ptr()) != 0)
|
||||
}
|
||||
let py = self.py();
|
||||
let attr_name = attr_name.into_py(py);
|
||||
|
||||
unsafe { Ok(ffi::PyObject_HasAttr(self.as_ptr(), attr_name.as_ptr()) != 0) }
|
||||
}
|
||||
|
||||
/// Retrieves an attribute value.
|
||||
///
|
||||
/// This is equivalent to the Python expression `self.attr_name`.
|
||||
///
|
||||
/// If calling this method becomes performance-critical, the [`intern!`] macro can be used
|
||||
/// to intern `attr_name`, thereby avoiding repeated temporary allocations of Python strings.
|
||||
/// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used
|
||||
/// to intern `attr_name`.
|
||||
///
|
||||
/// # Example: `intern!`ing the attribute name
|
||||
///
|
||||
|
@ -130,13 +134,14 @@ impl PyAny {
|
|||
/// ```
|
||||
pub fn getattr<N>(&self, attr_name: N) -> PyResult<&PyAny>
|
||||
where
|
||||
N: ToPyObject,
|
||||
N: IntoPy<Py<PyString>>,
|
||||
{
|
||||
let py = self.py();
|
||||
let attr_name = attr_name.into_py(py);
|
||||
|
||||
unsafe {
|
||||
self.py().from_owned_ptr_or_err(ffi::PyObject_GetAttr(
|
||||
self.as_ptr(),
|
||||
attr_name.to_object(self.py()).as_ptr(),
|
||||
))
|
||||
let ret = ffi::PyObject_GetAttr(self.as_ptr(), attr_name.as_ptr());
|
||||
py.from_owned_ptr_or_err(ret)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,8 +149,8 @@ impl PyAny {
|
|||
///
|
||||
/// This is equivalent to the Python expression `self.attr_name = value`.
|
||||
///
|
||||
/// If calling this method becomes performance-critical, the [`intern!`] macro can be used
|
||||
/// to intern `attr_name`, thereby avoiding repeated temporary allocations of Python strings.
|
||||
/// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used
|
||||
/// to intern `name`.
|
||||
///
|
||||
/// # Example: `intern!`ing the attribute name
|
||||
///
|
||||
|
@ -164,35 +169,35 @@ impl PyAny {
|
|||
/// ```
|
||||
pub fn setattr<N, V>(&self, attr_name: N, value: V) -> PyResult<()>
|
||||
where
|
||||
N: ToPyObject,
|
||||
N: IntoPy<Py<PyString>>,
|
||||
V: ToPyObject,
|
||||
{
|
||||
let py = self.py();
|
||||
let attr_name = attr_name.into_py(py);
|
||||
let value = value.to_object(py);
|
||||
|
||||
unsafe {
|
||||
err::error_on_minusone(
|
||||
py,
|
||||
ffi::PyObject_SetAttr(
|
||||
self.as_ptr(),
|
||||
attr_name.to_object(py).as_ptr(),
|
||||
value.to_object(py).as_ptr(),
|
||||
),
|
||||
)
|
||||
let ret = ffi::PyObject_SetAttr(self.as_ptr(), attr_name.as_ptr(), value.as_ptr());
|
||||
err::error_on_minusone(py, ret)
|
||||
}
|
||||
}
|
||||
|
||||
/// Deletes an attribute.
|
||||
///
|
||||
/// This is equivalent to the Python statement `del self.attr_name`.
|
||||
///
|
||||
/// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used
|
||||
/// to intern `attr_name`.
|
||||
pub fn delattr<N>(&self, attr_name: N) -> PyResult<()>
|
||||
where
|
||||
N: ToPyObject,
|
||||
N: IntoPy<Py<PyString>>,
|
||||
{
|
||||
let py = self.py();
|
||||
let attr_name = attr_name.into_py(py);
|
||||
|
||||
unsafe {
|
||||
err::error_on_minusone(
|
||||
self.py(),
|
||||
ffi::PyObject_DelAttr(self.as_ptr(), attr_name.to_object(py).as_ptr()),
|
||||
)
|
||||
let ret = ffi::PyObject_DelAttr(self.as_ptr(), attr_name.as_ptr());
|
||||
err::error_on_minusone(py, ret)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -273,6 +278,10 @@ impl PyAny {
|
|||
|
||||
/// Tests whether two Python objects obey a given [`CompareOp`].
|
||||
///
|
||||
/// [`lt`](Self::lt), [`le`](Self::le), [`eq`](Self::eq), [`ne`](Self::ne),
|
||||
/// [`gt`](Self::gt) and [`ge`](Self::ge) are the specialized versions
|
||||
/// of this function.
|
||||
///
|
||||
/// Depending on the value of `compare_op`, this is equivalent to one of the
|
||||
/// following Python expressions:
|
||||
///
|
||||
|
@ -408,22 +417,49 @@ impl PyAny {
|
|||
/// Calls the object.
|
||||
///
|
||||
/// This is equivalent to the Python expression `self(*args, **kwargs)`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use pyo3::prelude::*;
|
||||
/// use pyo3::types::PyDict;
|
||||
///
|
||||
/// const CODE: &str = r#"
|
||||
/// def function(*args, **kwargs):
|
||||
/// assert args == ("hello",)
|
||||
/// assert kwargs == {"cruel": "world"}
|
||||
/// return "called with args and kwargs"
|
||||
/// "#;
|
||||
///
|
||||
/// # fn main() -> PyResult<()> {
|
||||
/// Python::with_gil(|py| {
|
||||
/// let module = PyModule::from_code(py, CODE, "", "")?;
|
||||
/// let fun = module.getattr("function")?;
|
||||
/// let args = ("hello",);
|
||||
/// let kwargs = PyDict::new(py);
|
||||
/// kwargs.set_item("cruel", "world")?;
|
||||
/// let result = fun.call(args, Some(kwargs))?;
|
||||
/// assert_eq!(result.extract::<&str>()?, "called with args and kwargs");
|
||||
/// Ok(())
|
||||
/// })
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn call(
|
||||
&self,
|
||||
args: impl IntoPy<Py<PyTuple>>,
|
||||
kwargs: Option<&PyDict>,
|
||||
) -> PyResult<&PyAny> {
|
||||
let args = args.into_py(self.py()).into_ptr();
|
||||
let py = self.py();
|
||||
|
||||
let args = args.into_py(py);
|
||||
let kwargs = kwargs.into_ptr();
|
||||
let result = unsafe {
|
||||
let return_value = ffi::PyObject_Call(self.as_ptr(), args, kwargs);
|
||||
self.py().from_owned_ptr_or_err(return_value)
|
||||
};
|
||||
|
||||
unsafe {
|
||||
ffi::Py_XDECREF(args);
|
||||
let return_value = ffi::PyObject_Call(self.as_ptr(), args.as_ptr(), kwargs);
|
||||
let ret = py.from_owned_ptr_or_err(return_value);
|
||||
ffi::Py_XDECREF(kwargs);
|
||||
ret
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Calls the object without arguments.
|
||||
|
@ -468,25 +504,23 @@ impl PyAny {
|
|||
/// ```rust
|
||||
/// use pyo3::prelude::*;
|
||||
///
|
||||
/// const CODE: &str = r#"
|
||||
/// def function(*args, **kwargs):
|
||||
/// assert args == ("hello",)
|
||||
/// assert kwargs == {}
|
||||
/// return "called with args"
|
||||
/// "#;
|
||||
///
|
||||
/// # fn main() -> PyResult<()> {
|
||||
/// Python::with_gil(|py| -> PyResult<()> {
|
||||
/// let module = PyModule::import(py, "operator")?;
|
||||
/// let add = module.getattr("add")?;
|
||||
/// let args = (1, 2);
|
||||
/// let value = add.call1(args)?;
|
||||
/// assert_eq!(value.extract::<i32>()?, 3);
|
||||
/// Python::with_gil(|py| {
|
||||
/// let module = PyModule::from_code(py, CODE, "", "")?;
|
||||
/// let fun = module.getattr("function")?;
|
||||
/// let args = ("hello",);
|
||||
/// let result = fun.call1(args)?;
|
||||
/// assert_eq!(result.extract::<&str>()?, "called with args");
|
||||
/// Ok(())
|
||||
/// })?;
|
||||
/// # Ok(())}
|
||||
/// ```
|
||||
///
|
||||
/// This is equivalent to the following Python code:
|
||||
///
|
||||
/// ```python
|
||||
/// from operator import add
|
||||
///
|
||||
/// value = add(1,2)
|
||||
/// assert value == 3
|
||||
/// })
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn call1(&self, args: impl IntoPy<Py<PyTuple>>) -> PyResult<&PyAny> {
|
||||
self.call(args, None)
|
||||
|
@ -496,49 +530,51 @@ impl PyAny {
|
|||
///
|
||||
/// This is equivalent to the Python expression `self.name(*args, **kwargs)`.
|
||||
///
|
||||
/// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used
|
||||
/// to intern `name`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use pyo3::prelude::*;
|
||||
/// use pyo3::types::{IntoPyDict, PyList};
|
||||
/// use pyo3::types::PyDict;
|
||||
///
|
||||
/// const CODE: &str = r#"
|
||||
/// class A:
|
||||
/// def method(self, *args, **kwargs):
|
||||
/// assert args == ("hello",)
|
||||
/// assert kwargs == {"cruel": "world"}
|
||||
/// return "called with args and kwargs"
|
||||
/// a = A()
|
||||
/// "#;
|
||||
///
|
||||
/// # fn main() -> PyResult<()> {
|
||||
/// Python::with_gil(|py| -> PyResult<()> {
|
||||
/// let list = PyList::new(py, vec![3, 6, 5, 4, 7]);
|
||||
/// let kwargs = vec![("reverse", true)].into_py_dict(py);
|
||||
///
|
||||
/// list.call_method("sort", (), Some(kwargs))?;
|
||||
/// assert_eq!(list.extract::<Vec<i32>>()?, vec![7, 6, 5, 4, 3]);
|
||||
/// Python::with_gil(|py| {
|
||||
/// let module = PyModule::from_code(py, CODE, "", "")?;
|
||||
/// let instance = module.getattr("a")?;
|
||||
/// let args = ("hello",);
|
||||
/// let kwargs = PyDict::new(py);
|
||||
/// kwargs.set_item("cruel", "world")?;
|
||||
/// let result = instance.call_method("method", args, Some(kwargs))?;
|
||||
/// assert_eq!(result.extract::<&str>()?, "called with args and kwargs");
|
||||
/// Ok(())
|
||||
/// })?;
|
||||
/// # Ok(())}
|
||||
/// })
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// This is equivalent to the following Python code:
|
||||
///
|
||||
/// ```python
|
||||
/// my_list = [3, 6, 5, 4, 7]
|
||||
/// my_list.sort(reverse = True)
|
||||
/// assert my_list == [7, 6, 5, 4, 3]
|
||||
/// ```
|
||||
pub fn call_method(
|
||||
&self,
|
||||
name: &str,
|
||||
args: impl IntoPy<Py<PyTuple>>,
|
||||
kwargs: Option<&PyDict>,
|
||||
) -> PyResult<&PyAny> {
|
||||
unsafe {
|
||||
pub fn call_method<N, A>(&self, name: N, args: A, kwargs: Option<&PyDict>) -> PyResult<&PyAny>
|
||||
where
|
||||
N: IntoPy<Py<PyString>>,
|
||||
A: IntoPy<Py<PyTuple>>,
|
||||
{
|
||||
let py = self.py();
|
||||
let ptr = ffi::PyObject_GetAttr(self.as_ptr(), name.to_object(py).as_ptr());
|
||||
if ptr.is_null() {
|
||||
return Err(PyErr::fetch(py));
|
||||
}
|
||||
let args = args.into_py(py).into_ptr();
|
||||
|
||||
let callee = self.getattr(name)?;
|
||||
let args: Py<PyTuple> = args.into_py(py);
|
||||
let kwargs = kwargs.into_ptr();
|
||||
let result_ptr = ffi::PyObject_Call(ptr, args, kwargs);
|
||||
|
||||
unsafe {
|
||||
let result_ptr = ffi::PyObject_Call(callee.as_ptr(), args.as_ptr(), kwargs);
|
||||
let result = py.from_owned_ptr_or_err(result_ptr);
|
||||
ffi::Py_DECREF(ptr);
|
||||
ffi::Py_XDECREF(args);
|
||||
ffi::Py_XDECREF(kwargs);
|
||||
result
|
||||
}
|
||||
|
@ -548,39 +584,46 @@ impl PyAny {
|
|||
///
|
||||
/// This is equivalent to the Python expression `self.name()`.
|
||||
///
|
||||
/// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used
|
||||
/// to intern `name`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use pyo3::prelude::*;
|
||||
/// use pyo3::types::PyFloat;
|
||||
/// use std::f64::consts::PI;
|
||||
///
|
||||
/// const CODE: &str = r#"
|
||||
/// class A:
|
||||
/// def method(self, *args, **kwargs):
|
||||
/// assert args == ()
|
||||
/// assert kwargs == {}
|
||||
/// return "called with no arguments"
|
||||
/// a = A()
|
||||
/// "#;
|
||||
///
|
||||
/// # fn main() -> PyResult<()> {
|
||||
/// Python::with_gil(|py| -> PyResult<()> {
|
||||
/// let pi = PyFloat::new(py, PI);
|
||||
/// let ratio = pi.call_method0("as_integer_ratio")?;
|
||||
/// let (a, b) = ratio.extract::<(u64, u64)>()?;
|
||||
/// assert_eq!(a, 884_279_719_003_555);
|
||||
/// assert_eq!(b, 281_474_976_710_656);
|
||||
/// Python::with_gil(|py| {
|
||||
/// let module = PyModule::from_code(py, CODE, "", "")?;
|
||||
/// let instance = module.getattr("a")?;
|
||||
/// let result = instance.call_method0("method")?;
|
||||
/// assert_eq!(result.extract::<&str>()?, "called with no arguments");
|
||||
/// Ok(())
|
||||
/// })?;
|
||||
/// # Ok(())}
|
||||
/// })
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// This is equivalent to the following Python code:
|
||||
///
|
||||
/// ```python
|
||||
/// import math
|
||||
///
|
||||
/// a, b = math.pi.as_integer_ratio()
|
||||
/// ```
|
||||
pub fn call_method0(&self, name: &str) -> PyResult<&PyAny> {
|
||||
pub fn call_method0<N>(&self, name: N) -> PyResult<&PyAny>
|
||||
where
|
||||
N: IntoPy<Py<PyString>>,
|
||||
{
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(all(Py_3_9, not(any(Py_LIMITED_API, PyPy))))] {
|
||||
let py = self.py();
|
||||
|
||||
// Optimized path on python 3.9+
|
||||
unsafe {
|
||||
let name = name.into_py(self.py());
|
||||
self.py().from_owned_ptr_or_err(ffi::PyObject_CallMethodNoArgs(self.as_ptr(), name.as_ptr()))
|
||||
let name: Py<PyString> = name.into_py(py);
|
||||
let ptr = ffi::PyObject_CallMethodNoArgs(self.as_ptr(), name.as_ptr());
|
||||
py.from_owned_ptr_or_err(ptr)
|
||||
}
|
||||
} else {
|
||||
self.call_method(name, (), None)
|
||||
|
@ -592,30 +635,39 @@ impl PyAny {
|
|||
///
|
||||
/// This is equivalent to the Python expression `self.name(*args)`.
|
||||
///
|
||||
/// To avoid repeated temporary allocations of Python strings, the [`intern!`] macro can be used
|
||||
/// to intern `name`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use pyo3::prelude::*;
|
||||
/// use pyo3::types::PyList;
|
||||
///
|
||||
/// const CODE: &str = r#"
|
||||
/// class A:
|
||||
/// def method(self, *args, **kwargs):
|
||||
/// assert args == ("hello",)
|
||||
/// assert kwargs == {}
|
||||
/// return "called with args"
|
||||
/// a = A()
|
||||
/// "#;
|
||||
///
|
||||
/// # fn main() -> PyResult<()> {
|
||||
/// Python::with_gil(|py| -> PyResult<()> {
|
||||
/// let list = PyList::new(py, vec![1, 3, 4]);
|
||||
/// list.call_method1("insert", (1, 2))?;
|
||||
/// assert_eq!(list.extract::<Vec<u8>>()?, [1, 2, 3, 4]);
|
||||
/// Python::with_gil(|py| {
|
||||
/// let module = PyModule::from_code(py, CODE, "", "")?;
|
||||
/// let instance = module.getattr("a")?;
|
||||
/// let args = ("hello",);
|
||||
/// let result = instance.call_method1("method", args)?;
|
||||
/// assert_eq!(result.extract::<&str>()?, "called with args");
|
||||
/// Ok(())
|
||||
/// })?;
|
||||
/// # Ok(()) }
|
||||
/// })
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// This is equivalent to the following Python code:
|
||||
///
|
||||
/// ```python
|
||||
/// list_ = [1,3,4]
|
||||
/// list_.insert(1,2)
|
||||
/// assert list_ == [1,2,3,4]
|
||||
/// ```
|
||||
pub fn call_method1(&self, name: &str, args: impl IntoPy<Py<PyTuple>>) -> PyResult<&PyAny> {
|
||||
pub fn call_method1<N, A>(&self, name: N, args: A) -> PyResult<&PyAny>
|
||||
where
|
||||
N: IntoPy<Py<PyString>>,
|
||||
A: IntoPy<Py<PyTuple>>,
|
||||
{
|
||||
self.call_method(name, args, None)
|
||||
}
|
||||
|
||||
|
@ -712,7 +764,7 @@ impl PyAny {
|
|||
unsafe { ffi::Py_TYPE(self.as_ptr()) }
|
||||
}
|
||||
|
||||
/// Casts the PyObject to a concrete Python object type.
|
||||
/// Casts `self` to a concrete Python object type.
|
||||
///
|
||||
/// This can cast only to native Python types, not types implemented in Rust.
|
||||
pub fn cast_as<'a, D>(&'a self) -> Result<&'a D, PyDowncastError<'_>>
|
||||
|
@ -724,7 +776,7 @@ impl PyAny {
|
|||
|
||||
/// Extracts some type from the Python object.
|
||||
///
|
||||
/// This is a wrapper function around `FromPyObject::extract()`.
|
||||
/// This is a wrapper function around [`FromPyObject::extract()`].
|
||||
pub fn extract<'a, D>(&'a self) -> PyResult<D>
|
||||
where
|
||||
D: FromPyObject<'a>,
|
||||
|
@ -788,11 +840,11 @@ impl PyAny {
|
|||
unsafe { self.py().from_owned_ptr(ffi::PyObject_Dir(self.as_ptr())) }
|
||||
}
|
||||
|
||||
/// Checks whether this object is an instance of type `typ`.
|
||||
/// Checks whether this object is an instance of type `ty`.
|
||||
///
|
||||
/// This is equivalent to the Python expression `isinstance(self, typ)`.
|
||||
pub fn is_instance(&self, typ: &PyType) -> PyResult<bool> {
|
||||
let result = unsafe { ffi::PyObject_IsInstance(self.as_ptr(), typ.as_ptr()) };
|
||||
/// This is equivalent to the Python expression `isinstance(self, ty)`.
|
||||
pub fn is_instance(&self, ty: &PyType) -> PyResult<bool> {
|
||||
let result = unsafe { ffi::PyObject_IsInstance(self.as_ptr(), ty.as_ptr()) };
|
||||
err::error_on_minusone(self.py(), result)?;
|
||||
Ok(result == 1)
|
||||
}
|
||||
|
|
|
@ -155,7 +155,7 @@ impl PyCFunction {
|
|||
let (py, module) = py_or_module.into_py_and_maybe_module();
|
||||
let (mod_ptr, module_name) = if let Some(m) = module {
|
||||
let mod_ptr = m.as_ptr();
|
||||
let name = m.name()?.into_py(py);
|
||||
let name: Py<PyAny> = m.name()?.into_py(py);
|
||||
(mod_ptr, name.as_ptr())
|
||||
} else {
|
||||
(std::ptr::null_mut(), std::ptr::null_mut())
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::exceptions;
|
|||
use crate::ffi;
|
||||
use crate::pyclass::PyClass;
|
||||
use crate::types::{PyAny, PyCFunction, PyDict, PyList, PyString};
|
||||
use crate::{AsPyPointer, IntoPy, PyObject, Python};
|
||||
use crate::{AsPyPointer, IntoPy, Py, PyObject, Python};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::str;
|
||||
|
||||
|
@ -65,8 +65,11 @@ impl PyModule {
|
|||
/// ```python
|
||||
/// import antigravity
|
||||
/// ```
|
||||
pub fn import<'p>(py: Python<'p>, name: &str) -> PyResult<&'p PyModule> {
|
||||
let name: PyObject = name.into_py(py);
|
||||
pub fn import<N>(py: Python<'_>, name: N) -> PyResult<&PyModule>
|
||||
where
|
||||
N: IntoPy<Py<PyString>>,
|
||||
{
|
||||
let name: Py<PyString> = name.into_py(py);
|
||||
unsafe { py.from_owned_ptr_or_err(ffi::PyImport_Import(name.as_ptr())) }
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
use crate::exceptions::PyUnicodeDecodeError;
|
||||
use crate::types::PyBytes;
|
||||
use crate::{
|
||||
ffi, AsPyPointer, FromPyObject, IntoPy, PyAny, PyObject, PyResult, PyTryFrom, Python,
|
||||
ffi, AsPyPointer, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, PyTryFrom, Python,
|
||||
ToPyObject,
|
||||
};
|
||||
use std::borrow::Cow;
|
||||
|
@ -298,6 +298,13 @@ impl<'a> IntoPy<PyObject> for &'a str {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoPy<Py<PyString>> for &'a str {
|
||||
#[inline]
|
||||
fn into_py(self, py: Python<'_>) -> Py<PyString> {
|
||||
PyString::new(py, self).into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a Rust `Cow<'_, str>` to a Python object.
|
||||
/// See `PyString::new` for details on the conversion.
|
||||
impl<'a> ToPyObject for Cow<'a, str> {
|
||||
|
|
|
@ -49,11 +49,14 @@ impl PyTraceback {
|
|||
/// ```
|
||||
pub fn format(&self) -> PyResult<String> {
|
||||
let py = self.py();
|
||||
let string_io = py.import("io")?.getattr("StringIO")?.call0()?;
|
||||
let string_io = py
|
||||
.import(intern!(py, "io"))?
|
||||
.getattr(intern!(py, "StringIO"))?
|
||||
.call0()?;
|
||||
let result = unsafe { ffi::PyTraceBack_Print(self.as_ptr(), string_io.as_ptr()) };
|
||||
error_on_minusone(py, result)?;
|
||||
let formatted = string_io
|
||||
.getattr("getvalue")?
|
||||
.getattr(intern!(py, "getvalue"))?
|
||||
.call0()?
|
||||
.downcast::<PyString>()?
|
||||
.to_str()?
|
||||
|
|
|
@ -74,7 +74,7 @@ pub struct B {
|
|||
#[test]
|
||||
fn test_transparent_named_field_struct() {
|
||||
Python::with_gil(|py| {
|
||||
let test = "test".into_py(py);
|
||||
let test: PyObject = "test".into_py(py);
|
||||
let b: B = FromPyObject::extract(test.as_ref(py)).expect("Failed to extract B from String");
|
||||
assert_eq!(b.test, "test");
|
||||
let test: PyObject = 1.into_py(py);
|
||||
|
@ -92,7 +92,7 @@ pub struct D<T> {
|
|||
#[test]
|
||||
fn test_generic_transparent_named_field_struct() {
|
||||
Python::with_gil(|py| {
|
||||
let test = "test".into_py(py);
|
||||
let test: PyObject = "test".into_py(py);
|
||||
let d: D<String> =
|
||||
D::extract(test.as_ref(py)).expect("Failed to extract D<String> from String");
|
||||
assert_eq!(d.test, "test");
|
||||
|
@ -180,7 +180,7 @@ fn test_transparent_tuple_struct() {
|
|||
let tup: PyObject = 1.into_py(py);
|
||||
let tup = TransparentTuple::extract(tup.as_ref(py));
|
||||
assert!(tup.is_err());
|
||||
let test = "test".into_py(py);
|
||||
let test: PyObject = "test".into_py(py);
|
||||
let tup = TransparentTuple::extract(test.as_ref(py))
|
||||
.expect("Failed to extract TransparentTuple from PyTuple");
|
||||
assert_eq!(tup.0, "test");
|
||||
|
|
Loading…
Reference in New Issue