diff --git a/src/conversions/path.rs b/src/conversions/path.rs index a5f04a5db38..a82d2942bf3 100644 --- a/src/conversions/path.rs +++ b/src/conversions/path.rs @@ -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); diff --git a/src/err/mod.rs b/src/err/mod.rs index eed6798ad58..12dba87cb1f 100644 --- a/src/err/mod.rs +++ b/src/err/mod.rs @@ -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)); @@ -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" diff --git a/src/instance.rs b/src/instance.rs index a8c0997bf1f..9d510792b24 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -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, @@ -643,49 +643,56 @@ impl Py { /// 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( &self, py: Python<'_>, - name: &str, - args: impl IntoPy>, + name: N, + args: A, kwargs: Option<&PyDict>, - ) -> PyResult { - 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 + where + N: IntoPy>, + A: IntoPy>, + { + let name: Py = name.into_py(py); + let args: Py = 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>, - ) -> PyResult { + pub fn call_method1(&self, py: Python<'_>, name: N, args: A) -> PyResult + where + N: IntoPy>, + A: IntoPy>, + { 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 { + pub fn call_method0(&self, py: Python<'_>, name: N) -> PyResult + where + N: IntoPy>, + { 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 = name.into_py(py); PyObject::from_owned_ptr_or_err(py, ffi::PyObject_CallMethodNoArgs(self.as_ptr(), name.as_ptr())) } } else { diff --git a/src/marker.rs b/src/marker.rs index f159d89e85e..760586d109e 100644 --- a/src/marker.rs +++ b/src/marker.rs @@ -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; @@ -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(self, name: N) -> PyResult<&'py PyModule> + where + N: IntoPy>, + { PyModule::import(self, name) } diff --git a/src/types/any.rs b/src/types/any.rs index 9ba9b515944..9db980ad0fa 100644 --- a/src/types/any.rs +++ b/src/types/any.rs @@ -400,6 +400,33 @@ 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>, @@ -460,25 +487,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::()?, 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>) -> PyResult<&PyAny> { self.call(args, None) @@ -492,48 +517,51 @@ impl PyAny { /// /// ```rust /// use pyo3::prelude::*; - /// use pyo3::types::{IntoPyDict, PyList}; + /// use pyo3::types::PyDict; /// - /// # 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); + /// 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() + /// "#; /// - /// list.call_method("sort", (), Some(kwargs))?; - /// assert_eq!(list.extract::>()?, vec![7, 6, 5, 4, 3]); + /// # fn main() -> PyResult<()> { + /// 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>, - 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(&self, name: N, args: A, kwargs: Option<&PyDict>) -> PyResult<&PyAny> + where + N: IntoPy>, + A: IntoPy>, + { + let py = self.py(); + let name: Py = name.into_py(py); + let args: Py = 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. @@ -544,35 +572,39 @@ impl PyAny { /// /// ```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(&self, name: N) -> PyResult<&PyAny> + where + N: IntoPy>, + { 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 = 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) @@ -588,26 +620,32 @@ impl PyAny { /// /// ```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::>()?, [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>) -> PyResult<&PyAny> { + pub fn call_method1(&self, name: N, args: A) -> PyResult<&PyAny> + where + N: IntoPy>, + A: IntoPy>, + { self.call_method(name, args, None) } diff --git a/src/types/function.rs b/src/types/function.rs index 297b23c9242..667c8ceebba 100644 --- a/src/types/function.rs +++ b/src/types/function.rs @@ -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 = m.name()?.into_py(py); (mod_ptr, name.as_ptr()) } else { (std::ptr::null_mut(), std::ptr::null_mut()) diff --git a/src/types/module.rs b/src/types/module.rs index 2838734b473..0ee74355019 100644 --- a/src/types/module.rs +++ b/src/types/module.rs @@ -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; @@ -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(py: Python<'_>, name: N) -> PyResult<&PyModule> + where + N: IntoPy>, + { + let name: Py = name.into_py(py); unsafe { py.from_owned_ptr_or_err(ffi::PyImport_Import(name.as_ptr())) } } diff --git a/src/types/string.rs b/src/types/string.rs index 4e906f8ce51..413e1d193ce 100644 --- a/src/types/string.rs +++ b/src/types/string.rs @@ -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 for &'a str { } } +impl<'a> IntoPy> for &'a str { + #[inline] + fn into_py(self, py: Python<'_>) -> Py { + 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> { diff --git a/src/types/traceback.rs b/src/types/traceback.rs index 6d799a703f4..7e4393eb98d 100644 --- a/src/types/traceback.rs +++ b/src/types/traceback.rs @@ -49,11 +49,14 @@ impl PyTraceback { /// ``` pub fn format(&self) -> PyResult { 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::()? .to_str()? diff --git a/tests/test_frompyobject.rs b/tests/test_frompyobject.rs index 7215ba82e24..b93be185087 100644 --- a/tests/test_frompyobject.rs +++ b/tests/test_frompyobject.rs @@ -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 { #[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 = D::extract(test.as_ref(py)).expect("Failed to extract D 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");