Skip to content

Commit

Permalink
Allow more methods to take interned arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
mejrs committed Apr 21, 2022
1 parent dea9eb7 commit 8e2e3af
Show file tree
Hide file tree
Showing 10 changed files with 233 additions and 138 deletions.
7 changes: 4 additions & 3 deletions src/conversions/path.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::intern;
use crate::types::PyType;
use crate::{FromPyObject, IntoPy, PyAny, PyObject, PyResult, Python, ToPyObject};
use std::borrow::Cow;
Expand All @@ -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);
Expand Down
7 changes: 5 additions & 2 deletions src/err/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,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));
Expand All @@ -148,7 +151,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"
Expand Down
64 changes: 40 additions & 24 deletions src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::conversion::{PyTryFrom, ToBorrowedObject};
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, AsPyPointer, FromPyObject, IntoPy, IntoPyPointer, PyAny, PyClass, PyClassInitializer,
PyRef, PyRefMut, PyTypeInfo, Python, ToPyObject,
Expand Down Expand Up @@ -566,8 +566,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
///
Expand Down Expand Up @@ -643,49 +643,65 @@ 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> {
name.with_borrowed_ptr(py, |name| unsafe {
let args = args.into_py(py).into_ptr();
let kwargs = kwargs.into_ptr();
let ptr = ffi::PyObject_GetAttr(self.as_ptr(), name);
if ptr.is_null() {
return Err(PyErr::fetch(py));
}
) -> PyResult<PyObject>
where
N: IntoPy<Py<PyString>>,
A: IntoPy<Py<PyTuple>>,
{
let name: Py<PyString> = name.into_py(py);
let args: Py<PyTuple> = args.into_py(py);

let ptr = self.getattr(py, name)?.into_ptr();
let args = args.into_ptr();
let kwargs = kwargs.into_ptr();

unsafe {
let result = PyObject::from_owned_ptr_or_err(py, ffi::PyObject_Call(ptr, args, kwargs));
ffi::Py_DECREF(ptr);
ffi::Py_XDECREF(args);
ffi::Py_XDECREF(kwargs);
result
})
}
}

/// 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 {
Expand Down Expand Up @@ -714,7 +730,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.
Expand Down
11 changes: 8 additions & 3 deletions src/marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,11 @@ use crate::err::{self, PyDowncastError, PyErr, PyResult};
use crate::gil::{self, GILGuard, GILPool};
use crate::impl_::not_send::NotSend;
use crate::type_object::{PyTypeInfo, PyTypeObject};
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};
use crate::{
ffi, AsPyPointer, FromPyPointer, IntoPy, IntoPyPointer, Py, PyNativeType, PyObject, PyTryFrom,
};
use std::ffi::{CStr, CString};
use std::marker::PhantomData;
use std::os::raw::c_int;
Expand Down Expand Up @@ -589,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)
}

Expand Down
Loading

0 comments on commit 8e2e3af

Please sign in to comment.