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 20, 2022
1 parent d1e4f56 commit 4663d16
Show file tree
Hide file tree
Showing 10 changed files with 93 additions and 57 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
2 changes: 1 addition & 1 deletion src/err/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,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
49 changes: 28 additions & 21 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 @@ -643,49 +643,56 @@ 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(
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> {
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> {
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
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
48 changes: 29 additions & 19 deletions src/types/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -513,27 +513,27 @@ impl PyAny {
/// 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> {
name.with_borrowed_ptr(self.py(), |name| unsafe {
let py = self.py();
let ptr = ffi::PyObject_GetAttr(self.as_ptr(), name);
if ptr.is_null() {
return Err(PyErr::fetch(py));
}
let args = args.into_py(py).into_ptr();
let kwargs = kwargs.into_ptr();
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 name: Py<PyString> = name.into_py(py);
let args: Py<PyTuple> = args.into_py(py);

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

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

/// Calls a method on the object without arguments.
Expand Down Expand Up @@ -566,13 +566,19 @@ impl PyAny {
///
/// 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)
Expand Down Expand Up @@ -607,7 +613,11 @@ impl PyAny {
/// 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)
}

Expand Down
2 changes: 1 addition & 1 deletion src/types/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
9 changes: 6 additions & 3 deletions src/types/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::ffi;
use crate::pyclass::PyClass;
use crate::type_object::PyTypeObject;
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;

Expand Down Expand Up @@ -66,8 +66,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())) }
}

Expand Down
9 changes: 8 additions & 1 deletion src/types/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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> {
Expand Down
7 changes: 5 additions & 2 deletions src/types/traceback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()?
Expand Down
6 changes: 3 additions & 3 deletions tests/test_frompyobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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");
Expand Down Expand Up @@ -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");
Expand Down

0 comments on commit 4663d16

Please sign in to comment.