1use crate::{
2 builtins::{PyBaseExceptionRef, PyTupleRef, PyTypeRef},
3 convert::ToPyObject,
4 object::{Traverse, TraverseFn},
5 AsObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine,
6};
7use indexmap::IndexMap;
8use itertools::Itertools;
9use std::ops::RangeInclusive;
10
11pub trait IntoFuncArgs: Sized {
12 fn into_args(self, vm: &VirtualMachine) -> FuncArgs;
13 fn into_method_args(self, obj: PyObjectRef, vm: &VirtualMachine) -> FuncArgs {
14 let mut args = self.into_args(vm);
15 args.prepend_arg(obj);
16 args
17 }
18}
19
20impl<T> IntoFuncArgs for T
21where
22 T: Into<FuncArgs>,
23{
24 fn into_args(self, _vm: &VirtualMachine) -> FuncArgs {
25 self.into()
26 }
27}
28
29macro_rules! into_func_args_from_tuple {
32 ($(($n:tt, $T:ident)),*) => {
33 impl<$($T,)*> IntoFuncArgs for ($($T,)*)
34 where
35 $($T: ToPyObject,)*
36 {
37 #[inline]
38 fn into_args(self, vm: &VirtualMachine) -> FuncArgs {
39 let ($($n,)*) = self;
40 PosArgs::new(vec![$($n.to_pyobject(vm),)*]).into()
41 }
42
43 #[inline]
44 fn into_method_args(self, obj: PyObjectRef, vm: &VirtualMachine) -> FuncArgs {
45 let ($($n,)*) = self;
46 PosArgs::new(vec![obj, $($n.to_pyobject(vm),)*]).into()
47 }
48 }
49 };
50}
51
52into_func_args_from_tuple!((v1, T1));
53into_func_args_from_tuple!((v1, T1), (v2, T2));
54into_func_args_from_tuple!((v1, T1), (v2, T2), (v3, T3));
55into_func_args_from_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4));
56into_func_args_from_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4), (v5, T5));
57
58#[derive(Debug, Default, Clone, Traverse)]
62pub struct FuncArgs {
63 pub args: Vec<PyObjectRef>,
64 pub kwargs: IndexMap<String, PyObjectRef>,
66}
67
68unsafe impl Traverse for IndexMap<String, PyObjectRef> {
69 fn traverse(&self, tracer_fn: &mut TraverseFn) {
70 self.values().for_each(|v| v.traverse(tracer_fn));
71 }
72}
73
74impl<A> From<A> for FuncArgs
76where
77 A: Into<PosArgs>,
78{
79 fn from(args: A) -> Self {
80 FuncArgs {
81 args: args.into().into_vec(),
82 kwargs: IndexMap::new(),
83 }
84 }
85}
86
87impl From<KwArgs> for FuncArgs {
88 fn from(kwargs: KwArgs) -> Self {
89 FuncArgs {
90 args: Vec::new(),
91 kwargs: kwargs.0,
92 }
93 }
94}
95
96impl FromArgs for FuncArgs {
97 fn from_args(_vm: &VirtualMachine, args: &mut FuncArgs) -> Result<Self, ArgumentError> {
98 Ok(std::mem::take(args))
99 }
100}
101
102impl FuncArgs {
103 pub fn new<A, K>(args: A, kwargs: K) -> Self
104 where
105 A: Into<PosArgs>,
106 K: Into<KwArgs>,
107 {
108 let PosArgs(args) = args.into();
109 let KwArgs(kwargs) = kwargs.into();
110 Self { args, kwargs }
111 }
112
113 pub fn with_kwargs_names<A, KW>(mut args: A, kwarg_names: KW) -> Self
114 where
115 A: ExactSizeIterator<Item = PyObjectRef>,
116 KW: ExactSizeIterator<Item = String>,
117 {
118 let total_argc = args.len();
120 let kwargc = kwarg_names.len();
121 let posargc = total_argc - kwargc;
122
123 let posargs = args.by_ref().take(posargc).collect();
124
125 let kwargs = kwarg_names.zip_eq(args).collect::<IndexMap<_, _>>();
126
127 FuncArgs {
128 args: posargs,
129 kwargs,
130 }
131 }
132
133 pub fn is_empty(&self) -> bool {
134 self.args.is_empty() && self.kwargs.is_empty()
135 }
136
137 pub fn prepend_arg(&mut self, item: PyObjectRef) {
138 self.args.reserve_exact(1);
139 self.args.insert(0, item)
140 }
141
142 pub fn shift(&mut self) -> PyObjectRef {
143 self.args.remove(0)
144 }
145
146 pub fn get_kwarg(&self, key: &str, default: PyObjectRef) -> PyObjectRef {
147 self.kwargs
148 .get(key)
149 .cloned()
150 .unwrap_or_else(|| default.clone())
151 }
152
153 pub fn get_optional_kwarg(&self, key: &str) -> Option<PyObjectRef> {
154 self.kwargs.get(key).cloned()
155 }
156
157 pub fn get_optional_kwarg_with_type(
158 &self,
159 key: &str,
160 ty: PyTypeRef,
161 vm: &VirtualMachine,
162 ) -> PyResult<Option<PyObjectRef>> {
163 match self.get_optional_kwarg(key) {
164 Some(kwarg) => {
165 if kwarg.fast_isinstance(&ty) {
166 Ok(Some(kwarg))
167 } else {
168 let expected_ty_name = &ty.name();
169 let kwarg_class = kwarg.class();
170 let actual_ty_name = &kwarg_class.name();
171 Err(vm.new_type_error(format!(
172 "argument of type {expected_ty_name} is required for named parameter `{key}` (got: {actual_ty_name})"
173 )))
174 }
175 }
176 None => Ok(None),
177 }
178 }
179
180 pub fn take_positional(&mut self) -> Option<PyObjectRef> {
181 if self.args.is_empty() {
182 None
183 } else {
184 Some(self.args.remove(0))
185 }
186 }
187
188 pub fn take_positional_keyword(&mut self, name: &str) -> Option<PyObjectRef> {
189 self.take_positional().or_else(|| self.take_keyword(name))
190 }
191
192 pub fn take_keyword(&mut self, name: &str) -> Option<PyObjectRef> {
193 self.kwargs.swap_remove(name)
194 }
195
196 pub fn remaining_keywords(&mut self) -> impl Iterator<Item = (String, PyObjectRef)> + '_ {
197 self.kwargs.drain(..)
198 }
199
200 pub fn bind<T: FromArgs>(mut self, vm: &VirtualMachine) -> PyResult<T> {
209 let given_args = self.args.len();
210 let bound = T::from_args(vm, &mut self)
211 .map_err(|e| e.into_exception(T::arity(), given_args, vm))?;
212
213 if !self.args.is_empty() {
214 Err(vm.new_type_error(format!(
215 "Expected at most {} arguments ({} given)",
216 T::arity().end(),
217 given_args,
218 )))
219 } else if let Some(err) = self.check_kwargs_empty(vm) {
220 Err(err)
221 } else {
222 Ok(bound)
223 }
224 }
225
226 pub fn check_kwargs_empty(&self, vm: &VirtualMachine) -> Option<PyBaseExceptionRef> {
227 self.kwargs
228 .keys()
229 .next()
230 .map(|k| vm.new_type_error(format!("Unexpected keyword argument {k}")))
231 }
232}
233
234pub enum ArgumentError {
237 TooFewArgs,
239 TooManyArgs,
241 InvalidKeywordArgument(String),
243 RequiredKeywordArgument(String),
245 Exception(PyBaseExceptionRef),
248}
249
250impl From<PyBaseExceptionRef> for ArgumentError {
251 fn from(ex: PyBaseExceptionRef) -> Self {
252 ArgumentError::Exception(ex)
253 }
254}
255
256impl ArgumentError {
257 fn into_exception(
258 self,
259 arity: RangeInclusive<usize>,
260 num_given: usize,
261 vm: &VirtualMachine,
262 ) -> PyBaseExceptionRef {
263 match self {
264 ArgumentError::TooFewArgs => vm.new_type_error(format!(
265 "Expected at least {} arguments ({} given)",
266 arity.start(),
267 num_given
268 )),
269 ArgumentError::TooManyArgs => vm.new_type_error(format!(
270 "Expected at most {} arguments ({} given)",
271 arity.end(),
272 num_given
273 )),
274 ArgumentError::InvalidKeywordArgument(name) => {
275 vm.new_type_error(format!("{name} is an invalid keyword argument"))
276 }
277 ArgumentError::RequiredKeywordArgument(name) => {
278 vm.new_type_error(format!("Required keyqord only argument {name}"))
279 }
280 ArgumentError::Exception(ex) => ex,
281 }
282 }
283}
284
285pub trait FromArgs: Sized {
289 fn arity() -> RangeInclusive<usize> {
293 0..=0
294 }
295
296 fn from_args(vm: &VirtualMachine, args: &mut FuncArgs) -> Result<Self, ArgumentError>;
298}
299
300pub trait FromArgOptional {
301 type Inner: TryFromObject;
302 fn from_inner(x: Self::Inner) -> Self;
303}
304impl<T: TryFromObject> FromArgOptional for OptionalArg<T> {
305 type Inner = T;
306 fn from_inner(x: T) -> Self {
307 Self::Present(x)
308 }
309}
310impl<T: TryFromObject> FromArgOptional for T {
311 type Inner = Self;
312 fn from_inner(x: Self) -> Self {
313 x
314 }
315}
316
317#[derive(Clone)]
332pub struct KwArgs<T = PyObjectRef>(IndexMap<String, T>);
333
334unsafe impl<T> Traverse for KwArgs<T>
335where
336 T: Traverse,
337{
338 fn traverse(&self, tracer_fn: &mut TraverseFn) {
339 self.0.iter().map(|(_, v)| v.traverse(tracer_fn)).count();
340 }
341}
342
343impl<T> KwArgs<T> {
344 pub fn new(map: IndexMap<String, T>) -> Self {
345 KwArgs(map)
346 }
347
348 pub fn pop_kwarg(&mut self, name: &str) -> Option<T> {
349 self.0.swap_remove(name)
350 }
351
352 pub fn is_empty(self) -> bool {
353 self.0.is_empty()
354 }
355}
356impl<T> FromIterator<(String, T)> for KwArgs<T> {
357 fn from_iter<I: IntoIterator<Item = (String, T)>>(iter: I) -> Self {
358 KwArgs(iter.into_iter().collect())
359 }
360}
361impl<T> Default for KwArgs<T> {
362 fn default() -> Self {
363 KwArgs(IndexMap::new())
364 }
365}
366
367impl<T> FromArgs for KwArgs<T>
368where
369 T: TryFromObject,
370{
371 fn from_args(vm: &VirtualMachine, args: &mut FuncArgs) -> Result<Self, ArgumentError> {
372 let mut kwargs = IndexMap::new();
373 for (name, value) in args.remaining_keywords() {
374 kwargs.insert(name, value.try_into_value(vm)?);
375 }
376 Ok(KwArgs(kwargs))
377 }
378}
379
380impl<T> IntoIterator for KwArgs<T> {
381 type Item = (String, T);
382 type IntoIter = indexmap::map::IntoIter<String, T>;
383
384 fn into_iter(self) -> Self::IntoIter {
385 self.0.into_iter()
386 }
387}
388
389#[derive(Clone)]
398pub struct PosArgs<T = PyObjectRef>(Vec<T>);
399
400unsafe impl<T> Traverse for PosArgs<T>
401where
402 T: Traverse,
403{
404 fn traverse(&self, tracer_fn: &mut TraverseFn) {
405 self.0.traverse(tracer_fn)
406 }
407}
408
409impl<T> PosArgs<T> {
410 pub fn new(args: Vec<T>) -> Self {
411 Self(args)
412 }
413
414 pub fn into_vec(self) -> Vec<T> {
415 self.0
416 }
417
418 pub fn iter(&self) -> std::slice::Iter<T> {
419 self.0.iter()
420 }
421}
422
423impl<T> From<Vec<T>> for PosArgs<T> {
424 fn from(v: Vec<T>) -> Self {
425 Self(v)
426 }
427}
428
429impl From<()> for PosArgs<PyObjectRef> {
430 fn from(_args: ()) -> Self {
431 Self(Vec::new())
432 }
433}
434
435impl<T> AsRef<[T]> for PosArgs<T> {
436 fn as_ref(&self) -> &[T] {
437 &self.0
438 }
439}
440
441impl<T: PyPayload> PosArgs<PyRef<T>> {
442 pub fn into_tuple(self, vm: &VirtualMachine) -> PyTupleRef {
443 vm.ctx
444 .new_tuple(self.0.into_iter().map(Into::into).collect())
445 }
446}
447
448impl<T> FromArgs for PosArgs<T>
449where
450 T: TryFromObject,
451{
452 fn from_args(vm: &VirtualMachine, args: &mut FuncArgs) -> Result<Self, ArgumentError> {
453 let mut varargs = Vec::new();
454 while let Some(value) = args.take_positional() {
455 varargs.push(value.try_into_value(vm)?);
456 }
457 Ok(PosArgs(varargs))
458 }
459}
460
461impl<T> IntoIterator for PosArgs<T> {
462 type Item = T;
463 type IntoIter = std::vec::IntoIter<T>;
464
465 fn into_iter(self) -> Self::IntoIter {
466 self.0.into_iter()
467 }
468}
469
470impl<T> FromArgs for T
471where
472 T: TryFromObject,
473{
474 fn arity() -> RangeInclusive<usize> {
475 1..=1
476 }
477
478 fn from_args(vm: &VirtualMachine, args: &mut FuncArgs) -> Result<Self, ArgumentError> {
479 let value = args.take_positional().ok_or(ArgumentError::TooFewArgs)?;
480 Ok(value.try_into_value(vm)?)
481 }
482}
483
484#[derive(Debug, result_like::OptionLike, is_macro::Is)]
488pub enum OptionalArg<T = PyObjectRef> {
489 Present(T),
490 Missing,
491}
492
493unsafe impl<T> Traverse for OptionalArg<T>
494where
495 T: Traverse,
496{
497 fn traverse(&self, tracer_fn: &mut TraverseFn) {
498 match self {
499 OptionalArg::Present(ref o) => o.traverse(tracer_fn),
500 OptionalArg::Missing => (),
501 }
502 }
503}
504
505impl OptionalArg<PyObjectRef> {
506 pub fn unwrap_or_none(self, vm: &VirtualMachine) -> PyObjectRef {
507 self.unwrap_or_else(|| vm.ctx.none())
508 }
509}
510
511pub type OptionalOption<T = PyObjectRef> = OptionalArg<Option<T>>;
512
513impl<T> OptionalOption<T> {
514 #[inline]
515 pub fn flatten(self) -> Option<T> {
516 self.into_option().flatten()
517 }
518}
519
520impl<T> FromArgs for OptionalArg<T>
521where
522 T: TryFromObject,
523{
524 fn arity() -> RangeInclusive<usize> {
525 0..=1
526 }
527
528 fn from_args(vm: &VirtualMachine, args: &mut FuncArgs) -> Result<Self, ArgumentError> {
529 let r = if let Some(value) = args.take_positional() {
530 OptionalArg::Present(value.try_into_value(vm)?)
531 } else {
532 OptionalArg::Missing
533 };
534 Ok(r)
535 }
536}
537
538impl FromArgs for () {
541 fn from_args(_vm: &VirtualMachine, _args: &mut FuncArgs) -> Result<Self, ArgumentError> {
542 Ok(())
543 }
544}
545
546macro_rules! tuple_from_py_func_args {
553 ($($T:ident),+) => {
554 impl<$($T),+> FromArgs for ($($T,)+)
555 where
556 $($T: FromArgs),+
557 {
558 fn arity() -> RangeInclusive<usize> {
559 let mut min = 0;
560 let mut max = 0;
561 $(
562 let (start, end) = $T::arity().into_inner();
563 min += start;
564 max += end;
565 )+
566 min..=max
567 }
568
569 fn from_args(vm: &VirtualMachine, args: &mut FuncArgs) -> Result<Self, ArgumentError> {
570 Ok(($($T::from_args(vm, args)?,)+))
571 }
572 }
573 };
574}
575
576tuple_from_py_func_args!(A);
580tuple_from_py_func_args!(A, B);
581tuple_from_py_func_args!(A, B, C);
582tuple_from_py_func_args!(A, B, C, D);
583tuple_from_py_func_args!(A, B, C, D, E);
584tuple_from_py_func_args!(A, B, C, D, E, F);
585tuple_from_py_func_args!(A, B, C, D, E, F, G);
586tuple_from_py_func_args!(A, B, C, D, E, F, G, H);