2017-06-14 21:08:30 +00:00
|
|
|
// Copyright (c) 2017-present PyO3 Project and Contributors
|
2015-08-02 19:56:03 +00:00
|
|
|
//
|
2017-06-14 21:08:30 +00:00
|
|
|
// based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython
|
2015-08-02 19:56:03 +00:00
|
|
|
|
|
|
|
|
2017-05-25 03:31:51 +00:00
|
|
|
use ffi;
|
2017-05-29 04:19:29 +00:00
|
|
|
use python::Python;
|
2015-08-02 19:56:03 +00:00
|
|
|
use objects::{PyObject, PyTuple, PyDict, PyString, exc};
|
2017-05-28 05:45:48 +00:00
|
|
|
use conversion::RefFromPyObject;
|
2015-08-02 19:56:03 +00:00
|
|
|
use err::{self, PyResult};
|
|
|
|
|
2015-08-02 22:06:15 +00:00
|
|
|
/// Description of a python parameter; used for `parse_args()`.
|
2015-08-02 19:56:03 +00:00
|
|
|
pub struct ParamDescription<'a> {
|
2015-08-02 22:06:15 +00:00
|
|
|
/// The name of the parameter.
|
|
|
|
pub name: &'a str,
|
|
|
|
/// Whether the parameter is optional.
|
2017-06-14 05:37:26 +00:00
|
|
|
pub is_optional: bool,
|
|
|
|
/// Whether the parameter is optional.
|
|
|
|
pub kw_only: bool
|
2015-08-02 19:56:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse argument list
|
|
|
|
///
|
2015-08-02 22:06:15 +00:00
|
|
|
/// * fname: Name of the current function
|
|
|
|
/// * params: Declared parameters of the function
|
|
|
|
/// * args: Positional arguments
|
|
|
|
/// * kwargs: Keyword arguments
|
|
|
|
/// * output: Output array that receives the arguments.
|
|
|
|
/// Must have same length as `params` and must be initialized to `None`.
|
2017-05-25 03:31:51 +00:00
|
|
|
pub fn parse_args<'p>(py: Python<'p>,
|
|
|
|
fname: Option<&str>, params: &[ParamDescription],
|
2017-06-03 01:58:16 +00:00
|
|
|
args: &'p PyTuple, kwargs: Option<&'p PyDict>,
|
2017-05-25 03:31:51 +00:00
|
|
|
accept_args: bool, accept_kwargs: bool,
|
2017-06-03 01:58:16 +00:00
|
|
|
output: &mut[Option<PyObject>]) -> PyResult<()>
|
2015-08-02 19:56:03 +00:00
|
|
|
{
|
|
|
|
assert!(params.len() == output.len());
|
2017-05-16 05:24:06 +00:00
|
|
|
|
2017-06-03 01:58:16 +00:00
|
|
|
let nargs = args.len(py);
|
|
|
|
let nkeywords = kwargs.map_or(0, |d| d.len(py));
|
2017-05-18 07:05:49 +00:00
|
|
|
if !accept_args && (nargs + nkeywords > params.len()) {
|
2017-05-25 03:31:51 +00:00
|
|
|
return Err(err::PyErr::new::<exc::TypeError, _>(
|
2017-05-28 15:57:34 +00:00
|
|
|
py,
|
2015-08-02 19:56:03 +00:00
|
|
|
format!("{}{} takes at most {} argument{} ({} given)",
|
|
|
|
fname.unwrap_or("function"),
|
|
|
|
if fname.is_some() { "()" } else { "" },
|
|
|
|
params.len(),
|
|
|
|
if params.len() == 1 { "s" } else { "" },
|
|
|
|
nargs + nkeywords
|
2017-05-25 03:31:51 +00:00
|
|
|
)));
|
2015-08-02 19:56:03 +00:00
|
|
|
}
|
|
|
|
let mut used_keywords = 0;
|
|
|
|
// Iterate through the parameters and assign values to output:
|
|
|
|
for (i, (p, out)) in params.iter().zip(output).enumerate() {
|
2017-06-03 01:58:16 +00:00
|
|
|
match kwargs.and_then(|d| d.get_item(py, p.name)) {
|
2015-08-02 19:56:03 +00:00
|
|
|
Some(kwarg) => {
|
|
|
|
*out = Some(kwarg);
|
|
|
|
used_keywords += 1;
|
|
|
|
if i < nargs {
|
2017-05-28 05:45:48 +00:00
|
|
|
return Err(err::PyErr::new::<exc::TypeError, _>(
|
2017-05-28 15:57:34 +00:00
|
|
|
py,
|
2017-06-07 02:26:59 +00:00
|
|
|
format!("Argument given by name ('{}') and position ({})", p.name, i+1)));
|
2015-08-02 19:56:03 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
None => {
|
2017-06-14 21:08:30 +00:00
|
|
|
if p.kw_only {
|
|
|
|
return Err(err::PyErr::new::<exc::TypeError, _>(
|
|
|
|
py, format!("Required keywordargument ('{}') not found", p.name)));
|
|
|
|
}
|
2015-08-02 19:56:03 +00:00
|
|
|
if i < nargs {
|
2017-06-03 01:58:16 +00:00
|
|
|
*out = Some(args.get_item(py, i));
|
2015-08-02 19:56:03 +00:00
|
|
|
} else {
|
|
|
|
*out = None;
|
|
|
|
if !p.is_optional {
|
2017-05-25 03:31:51 +00:00
|
|
|
return Err(err::PyErr::new::<exc::TypeError, _>(
|
2017-05-28 15:57:34 +00:00
|
|
|
py,
|
2017-05-28 05:45:48 +00:00
|
|
|
format!("Required argument ('{}') (pos {}) not found",
|
|
|
|
p.name, i+1)));
|
2015-08-02 19:56:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-05-18 07:05:49 +00:00
|
|
|
if !accept_kwargs && used_keywords != nkeywords {
|
2015-08-02 19:56:03 +00:00
|
|
|
// check for extraneous keyword arguments
|
2017-06-03 01:58:16 +00:00
|
|
|
for (key, _value) in kwargs.unwrap().items(py) {
|
|
|
|
let key = try!(try!(key.cast_as::<PyString>(py)).to_string(py));
|
2015-08-02 19:56:03 +00:00
|
|
|
if !params.iter().any(|p| p.name == key) {
|
2017-05-28 05:45:48 +00:00
|
|
|
return Err(err::PyErr::new::<exc::TypeError, _>(
|
2017-05-28 15:57:34 +00:00
|
|
|
py,
|
2015-08-02 19:56:03 +00:00
|
|
|
format!("'{}' is an invalid keyword argument for this function",
|
|
|
|
key)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
2017-05-28 05:45:48 +00:00
|
|
|
|
2016-03-07 22:22:44 +00:00
|
|
|
#[inline]
|
|
|
|
#[doc(hidden)]
|
2017-06-03 01:58:16 +00:00
|
|
|
pub unsafe fn get_kwargs(py: Python, ptr: *mut ffi::PyObject) -> Option<PyDict> {
|
2016-03-07 22:22:44 +00:00
|
|
|
if ptr.is_null() {
|
|
|
|
None
|
|
|
|
} else {
|
2017-05-29 09:47:27 +00:00
|
|
|
Some(PyDict::from_borrowed_ptr(py, ptr))
|
2016-03-07 22:22:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-27 22:21:57 +00:00
|
|
|
#[doc(hidden)] // used in py_argparse_extract!() macro
|
2017-05-25 03:31:51 +00:00
|
|
|
pub fn with_extracted_or_default<'p, P: ?Sized, R, F>(
|
2017-06-03 01:58:16 +00:00
|
|
|
py: Python, obj: Option<&'p PyObject>, f: F, default: &'static P) -> PyResult<R>
|
2017-01-27 22:21:57 +00:00
|
|
|
where F: FnOnce(&P) -> PyResult<R>,
|
2017-05-25 03:31:51 +00:00
|
|
|
P: RefFromPyObject<'p>
|
2017-01-27 22:21:57 +00:00
|
|
|
{
|
|
|
|
match obj {
|
2017-06-03 01:58:16 +00:00
|
|
|
Some(obj) => match P::with_extracted(py, obj, f) {
|
2017-01-27 22:21:57 +00:00
|
|
|
Ok(result) => result,
|
|
|
|
Err(e) => Err(e)
|
|
|
|
},
|
|
|
|
None => f(default)
|
|
|
|
}
|
|
|
|
}
|