use crate::err::PyResult;
use crate::exceptions::TypeError;
use crate::init_once;
use crate::instance::PyNativeType;
use crate::types::{PyAny, PyDict, PyModule, PyTuple};
use crate::GILPool;
use crate::Python;
use crate::{ffi, IntoPy, PyObject};
use std::ptr;
#[derive(Debug)]
pub struct ParamDescription {
pub name: &'static str,
pub is_optional: bool,
pub kw_only: bool,
}
pub fn parse_fn_args<'p>(
fname: Option<&str>,
params: &[ParamDescription],
args: &'p PyTuple,
kwargs: Option<&'p PyDict>,
accept_args: bool,
accept_kwargs: bool,
output: &mut [Option<&'p PyAny>],
) -> PyResult<(&'p PyTuple, Option<&'p PyDict>)> {
let nargs = args.len();
let mut used_args = 0;
macro_rules! raise_error {
($s: expr $(,$arg:expr)*) => (return Err(TypeError::py_err(format!(
concat!("{} ", $s), fname.unwrap_or("function") $(,$arg)*
))))
}
let kwargs = match kwargs {
Some(k) => Some(k.copy()?),
None => None,
};
for (i, (p, out)) in params.iter().zip(output).enumerate() {
*out = match kwargs.and_then(|d| d.get_item(p.name)) {
Some(kwarg) => {
if i < nargs {
raise_error!("got multiple values for argument: {}", p.name)
}
kwargs.as_ref().unwrap().del_item(p.name).unwrap();
Some(kwarg)
}
None => {
if p.kw_only {
if !p.is_optional {
raise_error!("missing required keyword-only argument: {}", p.name)
}
None
} else if i < nargs {
used_args += 1;
Some(args.get_item(i))
} else {
if !p.is_optional {
raise_error!("missing required positional argument: {}", p.name)
}
None
}
}
}
}
let is_kwargs_empty = kwargs.as_ref().map_or(true, |dict| dict.is_empty());
if !accept_kwargs && !is_kwargs_empty {
let (key, _) = kwargs.unwrap().iter().next().unwrap();
raise_error!("got an unexpected keyword argument: {}", key)
}
if !accept_args && used_args < nargs {
raise_error!(
"takes at most {} positional argument{} ({} given)",
used_args,
if used_args == 1 { "" } else { "s" },
nargs
)
}
let args = if accept_args {
let py = args.py();
let slice = args.slice(used_args as isize, nargs as isize).into_py(py);
py.checked_cast_as(slice).unwrap()
} else {
args
};
let kwargs = if accept_kwargs && is_kwargs_empty {
None
} else {
kwargs
};
Ok((args, kwargs))
}
pub unsafe fn make_module(
name: &str,
doc: &str,
initializer: impl Fn(Python, &PyModule) -> PyResult<()>,
) -> *mut ffi::PyObject {
use crate::IntoPyPointer;
init_once();
#[cfg(py_sys_config = "WITH_THREAD")]
#[cfg(not(Py_3_7))]
ffi::PyEval_InitThreads();
static mut MODULE_DEF: ffi::PyModuleDef = ffi::PyModuleDef_INIT;
MODULE_DEF.m_name = name.as_ptr() as *const _;
let module = ffi::PyModule_Create(&mut MODULE_DEF);
if module.is_null() {
return module;
}
let py = Python::assume_gil_acquired();
let _pool = GILPool::new(py);
let module = match py.from_owned_ptr_or_err::<PyModule>(module) {
Ok(m) => m,
Err(e) => {
e.restore(py);
return ptr::null_mut();
}
};
module
.add("__doc__", doc)
.expect("Failed to add doc for module");
match initializer(py, module) {
Ok(_) => module.into_ptr(),
Err(e) => {
e.restore(py);
ptr::null_mut()
}
}
}
pub trait IntoPyResult<T> {
fn into_py_result(self) -> PyResult<T>;
}
impl<T: IntoPy<PyObject>> IntoPyResult<T> for T {
fn into_py_result(self) -> PyResult<T> {
Ok(self)
}
}
impl<T: IntoPy<PyObject>> IntoPyResult<T> for PyResult<T> {
fn into_py_result(self) -> PyResult<T> {
self
}
}