use crate::err::{PyErr, PyResult};
use crate::exceptions::TypeError;
use crate::instance::PyNativeType;
use crate::pyclass::PyClass;
use crate::types::{PyAny, PyDict, PyModule, PyTuple};
use crate::{ffi, GILPool, IntoPy, PyCell, Python};
use std::cell::UnsafeCell;
#[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)?;
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))
}
#[doc(hidden)]
pub struct ModuleDef(UnsafeCell<ffi::PyModuleDef>);
unsafe impl Sync for ModuleDef {}
impl ModuleDef {
pub const unsafe fn new(name: &'static str) -> Self {
let mut init = ffi::PyModuleDef_INIT;
init.m_name = name.as_ptr() as *const _;
ModuleDef(UnsafeCell::new(init))
}
pub unsafe fn make_module(
&'static self,
doc: &str,
initializer: impl Fn(Python, &PyModule) -> PyResult<()>,
) -> PyResult<*mut ffi::PyObject> {
#[cfg(py_sys_config = "WITH_THREAD")]
#[cfg(not(Py_3_7))]
ffi::PyEval_InitThreads();
let module = ffi::PyModule_Create(self.0.get());
let pool = GILPool::new();
let py = pool.python();
if module.is_null() {
return Err(crate::PyErr::fetch(py));
}
let module = py.from_owned_ptr_or_err::<PyModule>(module)?;
module.add("__doc__", doc)?;
initializer(py, module)?;
Ok(crate::IntoPyPointer::into_ptr(module))
}
}
#[doc(hidden)]
pub trait PyBaseTypeUtils {
type Dict;
type WeakRef;
type LayoutAsBase;
type BaseNativeType;
}
impl<T: PyClass> PyBaseTypeUtils for T {
type Dict = T::Dict;
type WeakRef = T::WeakRef;
type LayoutAsBase = crate::pycell::PyCellInner<T>;
type BaseNativeType = T::BaseNativeType;
}
#[doc(hidden)]
pub trait ExtractExt<'a> {
type Target: crate::FromPyObject<'a>;
}
impl<'a, T> ExtractExt<'a> for T
where
T: crate::FromPyObject<'a>,
{
type Target = T;
}
#[doc(hidden)]
pub trait TryFromPyCell<'a, T: PyClass>: Sized {
type Error: Into<PyErr>;
fn try_from_pycell(cell: &'a crate::PyCell<T>) -> Result<Self, Self::Error>;
}
impl<'a, T, R> TryFromPyCell<'a, T> for R
where
T: 'a + PyClass,
R: std::convert::TryFrom<&'a PyCell<T>>,
R::Error: Into<PyErr>,
{
type Error = R::Error;
fn try_from_pycell(cell: &'a crate::PyCell<T>) -> Result<Self, Self::Error> {
<R as std::convert::TryFrom<&'a PyCell<T>>>::try_from(cell)
}
}