/src/types/function.rs

https://github.com/PyO3/pyo3 · Rust · 92 lines · 77 code · 9 blank · 6 comment · 3 complexity · 87d5bd32fc786f983b65ff8d8c5ebedc MD5 · raw file

  1. use std::ffi::{CStr, CString};
  2. use crate::derive_utils::PyFunctionArguments;
  3. use crate::exceptions::PyValueError;
  4. use crate::prelude::*;
  5. use crate::{class, ffi, AsPyPointer, PyMethodType};
  6. /// Represents a builtin Python function object.
  7. #[repr(transparent)]
  8. pub struct PyCFunction(PyAny);
  9. pyobject_native_var_type!(PyCFunction, ffi::PyCFunction_Type, ffi::PyCFunction_Check);
  10. impl PyCFunction {
  11. /// Create a new built-in function with keywords.
  12. ///
  13. /// See [raw_pycfunction] for documentation on how to get the `fun` argument.
  14. pub fn new_with_keywords<'a>(
  15. fun: ffi::PyCFunctionWithKeywords,
  16. name: &str,
  17. doc: &'static str,
  18. py_or_module: PyFunctionArguments<'a>,
  19. ) -> PyResult<&'a PyCFunction> {
  20. let fun = PyMethodType::PyCFunctionWithKeywords(fun);
  21. Self::new_(fun, name, doc, py_or_module)
  22. }
  23. /// Create a new built-in function without keywords.
  24. pub fn new<'a>(
  25. fun: ffi::PyCFunction,
  26. name: &str,
  27. doc: &'static str,
  28. py_or_module: PyFunctionArguments<'a>,
  29. ) -> PyResult<&'a PyCFunction> {
  30. let fun = PyMethodType::PyCFunction(fun);
  31. Self::new_(fun, name, doc, py_or_module)
  32. }
  33. fn new_<'a>(
  34. fun: class::PyMethodType,
  35. name: &str,
  36. doc: &'static str,
  37. py_or_module: PyFunctionArguments<'a>,
  38. ) -> PyResult<&'a PyCFunction> {
  39. let (py, module) = py_or_module.into_py_and_maybe_module();
  40. let doc: &'static CStr = CStr::from_bytes_with_nul(doc.as_bytes())
  41. .map_err(|_| PyValueError::new_err("docstring must end with NULL byte."))?;
  42. let name = CString::new(name.as_bytes()).map_err(|_| {
  43. PyValueError::new_err("Function name cannot contain contain NULL byte.")
  44. })?;
  45. let def = match fun {
  46. PyMethodType::PyCFunction(fun) => ffi::PyMethodDef {
  47. ml_name: name.into_raw() as _,
  48. ml_meth: Some(fun),
  49. ml_flags: ffi::METH_VARARGS,
  50. ml_doc: doc.as_ptr() as _,
  51. },
  52. PyMethodType::PyCFunctionWithKeywords(fun) => ffi::PyMethodDef {
  53. ml_name: name.into_raw() as _,
  54. ml_meth: Some(unsafe { std::mem::transmute(fun) }),
  55. ml_flags: ffi::METH_VARARGS | ffi::METH_KEYWORDS,
  56. ml_doc: doc.as_ptr() as _,
  57. },
  58. _ => {
  59. return Err(PyValueError::new_err(
  60. "Only PyCFunction and PyCFunctionWithKeywords are valid.",
  61. ))
  62. }
  63. };
  64. let (mod_ptr, module_name) = if let Some(m) = module {
  65. let mod_ptr = m.as_ptr();
  66. let name = m.name()?.into_py(py);
  67. (mod_ptr, name.as_ptr())
  68. } else {
  69. (std::ptr::null_mut(), std::ptr::null_mut())
  70. };
  71. unsafe {
  72. py.from_owned_ptr_or_err::<PyCFunction>(ffi::PyCFunction_NewEx(
  73. Box::into_raw(Box::new(def)),
  74. mod_ptr,
  75. module_name,
  76. ))
  77. }
  78. }
  79. }
  80. /// Represents a Python function object.
  81. #[repr(transparent)]
  82. pub struct PyFunction(PyAny);
  83. pyobject_native_var_type!(PyFunction, ffi::PyFunction_Type, ffi::PyFunction_Check);