1use super::signature::Param;
2use crate::{
3 AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine,
4 builtins::{PyBaseExceptionRef, PyTupleRef, PyType},
5 common::wtf8::{Wtf8, Wtf8Buf},
6 convert::ToPyObject,
7 object::{Traverse, TraverseFn},
8};
9use core::ops::{Deref, DerefMut, RangeInclusive};
10use indexmap::IndexMap;
11use itertools::Itertools;
12use std::hash::DefaultHasher;
13
14pub trait IntoFuncArgs: Sized {
15 fn into_args(self, vm: &VirtualMachine) -> FuncArgs;
16 fn into_method_args(self, obj: PyObjectRef, vm: &VirtualMachine) -> FuncArgs {
17 let mut args = self.into_args(vm);
18 let mut with_obj = Vec::with_capacity(args.args.len() + 1);
20 with_obj.push(obj);
21 with_obj.append(&mut args.args);
22 args.args = with_obj;
23 args
24 }
25}
26
27impl<T> IntoFuncArgs for T
28where
29 T: Into<FuncArgs>,
30{
31 fn into_args(self, _vm: &VirtualMachine) -> FuncArgs {
32 self.into()
33 }
34}
35
36macro_rules! into_func_args_from_tuple {
39 ($(($n:tt, $T:ident)),*) => {
40 impl<$($T,)*> IntoFuncArgs for ($($T,)*)
41 where
42 $($T: ToPyObject,)*
43 {
44 #[inline]
45 fn into_args(self, vm: &VirtualMachine) -> FuncArgs {
46 let ($($n,)*) = self;
47 PosArgs::new(vec![$($n.to_pyobject(vm),)*]).into()
48 }
49
50 #[inline]
51 fn into_method_args(self, obj: PyObjectRef, vm: &VirtualMachine) -> FuncArgs {
52 let ($($n,)*) = self;
53 PosArgs::new(vec![obj, $($n.to_pyobject(vm),)*]).into()
54 }
55 }
56 };
57}
58
59into_func_args_from_tuple!((v1, T1));
60into_func_args_from_tuple!((v1, T1), (v2, T2));
61into_func_args_from_tuple!((v1, T1), (v2, T2), (v3, T3));
62into_func_args_from_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4));
63into_func_args_from_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4), (v5, T5));
64into_func_args_from_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4), (v5, T5), (v6, T6));
65#[derive(Debug, Default, Clone, Traverse)]
74pub struct FuncArgs {
75 pub args: Vec<PyObjectRef>,
76 pub kwargs: KwArgs,
78}
79
80impl<A> From<A> for FuncArgs
82where
83 A: Into<PosArgs>,
84{
85 fn from(args: A) -> Self {
86 Self {
87 args: args.into().into_vec(),
88 ..Default::default()
89 }
90 }
91}
92
93impl<Name: ArgName> From<KwArgs<PyObjectRef, Name>> for FuncArgs {
94 fn from(kwargs: KwArgs<PyObjectRef, Name>) -> Self {
95 Self {
96 kwargs: KwArgs::new(kwargs.0),
97 ..Default::default()
98 }
99 }
100}
101
102impl FromArgs for FuncArgs {
103 const PARAMS: Option<&'static [Param]> =
104 Some(&[Param::var_positional("args"), Param::var_keyword("kwargs")]);
105
106 fn from_args(_vm: &VirtualMachine, args: &mut FuncArgs) -> Result<Self, ArgumentError> {
107 Ok(core::mem::take(args))
108 }
109}
110
111impl FuncArgs {
112 pub fn new<A, K>(args: A, kwargs: K) -> Self
113 where
114 A: Into<PosArgs>,
115 K: Into<KwArgs>,
116 {
117 let PosArgs(args, _) = args.into();
118 Self {
119 args,
120 kwargs: kwargs.into(),
121 }
122 }
123
124 pub fn with_kwargs_names<A, KW>(mut args: A, kwarg_names: KW) -> Self
125 where
126 A: ExactSizeIterator<Item = PyObjectRef>,
127 KW: ExactSizeIterator<Item = String>,
128 {
129 let total_argc = args.len();
131 let kwarg_count = kwarg_names.len();
132 let pos_arg_count = total_argc - kwarg_count;
133
134 let pos_args = args.by_ref().take(pos_arg_count).collect();
135
136 let kwargs = kwarg_names.zip_eq(args).collect();
137
138 Self {
139 args: pos_args,
140 kwargs,
141 }
142 }
143
144 #[must_use]
149 pub fn from_vectorcall(
150 args: &[PyObjectRef],
151 nargs: usize,
152 kwnames: Option<&[PyObjectRef]>,
153 ) -> Self {
154 debug_assert!(nargs <= args.len());
155 debug_assert!(kwnames.is_none_or(|kw| nargs + kw.len() <= args.len()));
156
157 let pos_args = args[..nargs].to_vec();
158
159 let kwargs = kwnames.map_or_else(KwArgs::default, |names| {
160 names
161 .iter()
162 .zip(&args[nargs..nargs + names.len()])
163 .map(|(name, val)| {
164 let key = name
167 .downcast_ref::<crate::builtins::PyStr>()
168 .expect("kwnames must be strings")
169 .as_wtf8()
170 .to_owned();
171 (key, val.clone())
172 })
173 .collect()
174 });
175
176 Self {
177 args: pos_args,
178 kwargs,
179 }
180 }
181
182 #[must_use]
184 pub fn from_vectorcall_owned(
185 mut args: Vec<PyObjectRef>,
186 nargs: usize,
187 kwnames: Option<&[PyObjectRef]>,
188 ) -> Self {
189 debug_assert!(nargs <= args.len());
190 debug_assert!(kwnames.is_none_or(|kw| nargs + kw.len() <= args.len()));
191 let kwargs = kwnames.map_or_else(KwArgs::default, |names| {
192 let kw_count = names.len();
193 names
194 .iter()
195 .zip(args.drain(nargs..nargs + kw_count))
196 .map(|(name, val)| {
197 let key = name
198 .downcast_ref::<crate::builtins::PyStr>()
199 .expect("kwnames must be strings")
200 .as_wtf8()
201 .to_owned();
202 (key, val)
203 })
204 .collect()
205 });
206
207 args.truncate(nargs);
208 Self { args, kwargs }
209 }
210
211 #[must_use]
212 pub fn is_empty(&self) -> bool {
213 self.args.is_empty() && self.kwargs.is_empty()
214 }
215
216 pub fn prepend_arg(&mut self, item: PyObjectRef) {
217 self.args.reserve(1);
220 self.args.insert(0, item)
221 }
222
223 pub fn shift(&mut self) -> PyObjectRef {
224 self.args.remove(0)
225 }
226
227 #[must_use]
228 pub fn get_kwarg(&self, key: &str, default: &PyObject) -> PyObjectRef {
229 self.kwargs
230 .get(key)
231 .cloned()
232 .unwrap_or_else(|| default.to_owned())
233 }
234
235 #[must_use]
236 pub fn get_optional_kwarg(&self, key: &str) -> Option<PyObjectRef> {
237 self.kwargs.get(key).cloned()
238 }
239
240 pub fn get_optional_kwarg_with_type(
241 &self,
242 key: &str,
243 ty: &Py<PyType>,
244 vm: &VirtualMachine,
245 ) -> PyResult<Option<PyObjectRef>> {
246 match self.get_optional_kwarg(key) {
247 Some(kwarg) => {
248 if kwarg.fast_isinstance(ty) {
249 Ok(Some(kwarg))
250 } else {
251 let expected_ty_name = &ty.name();
252 let kwarg_class = kwarg.class();
253 let actual_ty_name = &kwarg_class.name();
254 Err(vm.new_type_error(format!(
255 "argument of type {expected_ty_name} is required for named parameter `{key}` (got: {actual_ty_name})"
256 )))
257 }
258 }
259 None => Ok(None),
260 }
261 }
262
263 pub fn take_positional(&mut self) -> Option<PyObjectRef> {
264 if self.args.is_empty() {
265 None
266 } else {
267 Some(self.args.remove(0))
268 }
269 }
270
271 pub fn take_positional_keyword(&mut self, name: &str) -> Option<PyObjectRef> {
272 self.take_positional().or_else(|| self.take_keyword(name))
273 }
274
275 pub fn take_keyword(&mut self, name: &str) -> Option<PyObjectRef> {
276 self.kwargs.swap_remove(name)
277 }
278
279 pub fn remaining_keywords(&mut self) -> impl Iterator<Item = (Wtf8Buf, PyObjectRef)> + '_ {
280 self.kwargs.drain(..)
281 }
282
283 pub fn bind<T: FromArgs>(self, vm: &VirtualMachine) -> PyResult<T> {
292 self.bind_for(vm, Callee::default())
293 }
294
295 pub fn bind_for<T: FromArgs>(
298 mut self,
299 vm: &VirtualMachine,
300 callee: impl Into<Callee>,
301 ) -> PyResult<T> {
302 let callee = callee.into();
303 let instance = callee.instance_args();
306 let arity = T::arity();
307 let arity = arity.start().saturating_sub(instance)..=arity.end().saturating_sub(instance);
308 let num_given = self.args.len().saturating_sub(instance);
309
310 let bound = T::from_args(vm, &mut self)
311 .map_err(|e| e.into_exception(&arity, num_given, callee, vm))?;
312
313 if !self.args.is_empty() {
314 Err(ArgumentError::TooManyArgs.into_exception(&arity, num_given, callee, vm))
315 } else if let Some(err) = self.check_kwargs_empty_for(vm, callee) {
316 Err(err)
317 } else {
318 Ok(bound)
319 }
320 }
321
322 pub fn check_kwargs_empty(&self, vm: &VirtualMachine) -> Option<PyBaseExceptionRef> {
323 self.check_kwargs_empty_for(vm, Callee::default())
324 }
325
326 pub fn check_kwargs_empty_for(
329 &self,
330 vm: &VirtualMachine,
331 callee: impl Into<Callee>,
332 ) -> Option<PyBaseExceptionRef> {
333 let callee = callee.into();
334 self.kwargs
335 .keys()
336 .next()
337 .map(|k| callee.unexpected_keyword(&k.to_string(), vm))
338 }
339}
340
341#[derive(Clone, Copy, Debug, Default)]
349pub struct Callee {
350 name: Option<&'static str>,
351 instance_arg: bool,
352}
353
354impl From<&'static str> for Callee {
355 fn from(name: &'static str) -> Self {
356 Self::named(name)
357 }
358}
359
360impl Callee {
361 #[must_use]
363 pub const fn named(name: &'static str) -> Self {
364 Self {
365 name: Some(name),
366 instance_arg: false,
367 }
368 }
369
370 #[must_use]
372 pub fn for_type(class: &crate::Py<crate::builtins::PyType>) -> Self {
373 Self::named(class.slots.name)
374 }
375
376 #[must_use]
378 pub fn of<T: crate::PyPayload>(vm: &VirtualMachine) -> Self {
379 Self::for_type(T::class(&vm.ctx))
380 }
381
382 #[must_use]
384 pub const fn with_instance_arg(mut self, instance_arg: bool) -> Self {
385 self.instance_arg = instance_arg;
386 self
387 }
388
389 const fn instance_args(self) -> usize {
392 self.instance_arg as usize
393 }
394
395 fn wrong_arity(
397 self,
398 arity: &RangeInclusive<usize>,
399 too_few: bool,
400 num_given: usize,
401 vm: &VirtualMachine,
402 ) -> PyBaseExceptionRef {
403 vm.new_type_error(arity_message(self.name, arity, too_few, num_given))
404 }
405
406 fn unexpected_keyword(self, keyword: &str, vm: &VirtualMachine) -> PyBaseExceptionRef {
408 vm.new_type_error(unexpected_keyword_message(self.name, keyword))
409 }
410
411 fn missing_argument(
413 self,
414 keyword: &str,
415 pos: usize,
416 vm: &VirtualMachine,
417 ) -> PyBaseExceptionRef {
418 vm.new_type_error(missing_argument_message(self.name, keyword, pos))
419 }
420}
421
422fn short_name(name: &str) -> &str {
425 name.rsplit_once('.').map_or(name, |(_, name)| name)
426}
427
428fn call_form<'a>(name: Option<&'a str>, unnamed: &'a str) -> (&'a str, &'static str) {
431 match name.map(short_name) {
432 Some(name) => (name, "()"),
433 None => (unnamed, ""),
434 }
435}
436
437pub(crate) fn arity_message(
439 name: Option<&str>,
440 arity: &RangeInclusive<usize>,
441 too_few: bool,
442 num_given: usize,
443) -> String {
444 let (limit, bound) = if too_few {
445 (*arity.start(), "at least ")
446 } else {
447 (*arity.end(), "at most ")
448 };
449 let bound = if arity.start() == arity.end() {
450 ""
451 } else {
452 bound
453 };
454 let plural = if limit == 1 { "" } else { "s" };
455 let name = name
456 .map(short_name)
457 .map_or_else(String::new, |name| format!("{name} "));
458 format!("{name}expected {bound}{limit} argument{plural}, got {num_given}")
459}
460
461pub(crate) fn unexpected_keyword_message(name: Option<&str>, keyword: &str) -> String {
463 let (name, parens) = call_form(name, "this function");
464 format!("{name}{parens} got an unexpected keyword argument '{keyword}'")
465}
466
467pub(crate) fn missing_argument_message(name: Option<&str>, keyword: &str, pos: usize) -> String {
469 let (name, parens) = call_form(name, "function");
470 format!("{name}{parens} missing required argument '{keyword}' (pos {pos})")
471}
472
473pub enum ArgumentError {
476 TooFewArgs,
478 TooManyArgs,
480 InvalidKeywordArgument(String),
482 MissingRequiredArgument { name: String, pos: usize },
485 Exception(PyBaseExceptionRef),
488}
489
490impl From<PyBaseExceptionRef> for ArgumentError {
491 fn from(ex: PyBaseExceptionRef) -> Self {
492 Self::Exception(ex)
493 }
494}
495
496impl ArgumentError {
497 fn into_exception(
498 self,
499 arity: &RangeInclusive<usize>,
500 num_given: usize,
501 callee: Callee,
502 vm: &VirtualMachine,
503 ) -> PyBaseExceptionRef {
504 match self {
505 Self::TooFewArgs => callee.wrong_arity(arity, true, num_given, vm),
506 Self::TooManyArgs => callee.wrong_arity(arity, false, num_given, vm),
507 Self::InvalidKeywordArgument(name) => callee.unexpected_keyword(&name, vm),
508 Self::MissingRequiredArgument { name, pos } => callee.missing_argument(&name, pos, vm),
509 Self::Exception(ex) => ex,
510 }
511 }
512}
513
514pub trait FromArgs: Sized {
518 #[must_use]
522 fn arity() -> RangeInclusive<usize> {
523 0..=0
524 }
525
526 const PARAMS: Option<&'static [Param]> = None;
533
534 fn from_args(vm: &VirtualMachine, args: &mut FuncArgs) -> Result<Self, ArgumentError>;
536}
537
538pub trait FromArgOptional {
539 type Inner: TryFromObject;
540 fn from_inner(x: Self::Inner) -> Self;
541}
542
543pub trait OptionalArgDefault {
547 const PY_DEFAULT: super::signature::DefaultRepr;
548}
549
550impl<T> OptionalArgDefault for Option<T> {
551 const PY_DEFAULT: super::signature::DefaultRepr = super::signature::DefaultRepr::None;
552}
553
554impl<T> OptionalArgDefault for OptionalArg<T> {
555 const PY_DEFAULT: super::signature::DefaultRepr =
556 super::signature::DefaultRepr::Unrepresentable;
557}
558
559impl<T: TryFromObject> FromArgOptional for OptionalArg<T> {
560 type Inner = T;
561 fn from_inner(x: T) -> Self {
562 Self::Present(x)
563 }
564}
565
566impl<T: TryFromObject> FromArgOptional for T {
567 type Inner = Self;
568 fn from_inner(x: Self) -> Self {
569 x
570 }
571}
572
573pub trait ArgName {
592 const NAME: &'static str;
593}
594
595macro_rules! arg_name {
596 ($($ty:ident = $name:literal),* $(,)?) => {$(
597 #[derive(Clone, Copy, Debug)]
598 pub struct $ty;
599 impl ArgName for $ty {
600 const NAME: &'static str = $name;
601 }
602 )*};
603}
604
605arg_name! {
606 NameArgs = "args",
607 NameKwargs = "kwargs",
608 NameOthers = "others",
609 NameCoordinates = "coordinates",
610 NameIntegers = "integers",
611 NameChanges = "changes",
612 NameExcInfo = "exc_info",
613 NameKwds = "kwds",
614 NameKws = "kws",
615 NameObjs = "objs",
616 NameIterables = "iterables",
617 NameKeywords = "keywords",
618 NameFields = "fields",
619}
620
621#[derive(Clone, Debug)]
622pub struct KwArgs<T = PyObjectRef, Name: ArgName = NameKwargs>(
623 KwArgsMap<T>,
624 core::marker::PhantomData<Name>,
625);
626
627pub type KwArgsMap<T> = IndexMap<Wtf8Buf, T, core::hash::BuildHasherDefault<DefaultHasher>>;
634
635impl<T> Default for KwArgs<T> {
636 fn default() -> Self {
637 Self(KwArgsMap::default(), core::marker::PhantomData)
638 }
639}
640
641impl<T, Name: ArgName> Deref for KwArgs<T, Name> {
642 type Target = KwArgsMap<T>;
643
644 fn deref(&self) -> &Self::Target {
645 &self.0
646 }
647}
648
649impl<T, Name: ArgName> DerefMut for KwArgs<T, Name> {
650 fn deref_mut(&mut self) -> &mut Self::Target {
651 &mut self.0
652 }
653}
654
655unsafe impl<T, Name: ArgName> Traverse for KwArgs<T, Name>
656where
657 T: Traverse,
658{
659 fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
660 self.values().for_each(|v| v.traverse(tracer_fn));
661 }
662}
663
664impl<T> KwArgs<T> {
665 #[must_use]
666 pub const fn new(map: KwArgsMap<T>) -> Self {
667 Self(map, core::marker::PhantomData)
668 }
669}
670
671impl<T, Name: ArgName> KwArgs<T, Name> {
672 #[must_use]
676 pub fn get(&self, name: &str) -> Option<&T> {
677 self.0.get(Wtf8::new(name))
678 }
679
680 #[must_use]
681 pub fn contains_key(&self, name: &str) -> bool {
682 self.0.contains_key(Wtf8::new(name))
683 }
684
685 pub fn swap_remove(&mut self, name: &str) -> Option<T> {
686 self.0.swap_remove(Wtf8::new(name))
687 }
688
689 pub fn shift_remove(&mut self, name: &str) -> Option<T> {
690 self.0.shift_remove(Wtf8::new(name))
691 }
692
693 pub fn pop_kwarg(&mut self, name: &str) -> Option<T> {
694 self.swap_remove(name)
695 }
696
697 #[must_use]
698 pub fn into_default(self) -> KwArgs<T> {
699 KwArgs(self.0, core::marker::PhantomData)
700 }
701}
702
703impl<K: Into<Wtf8Buf>, T> FromIterator<(K, T)> for KwArgs<T> {
706 fn from_iter<I: IntoIterator<Item = (K, T)>>(iter: I) -> Self {
707 Self(
708 iter.into_iter().map(|(k, v)| (k.into(), v)).collect(),
709 core::marker::PhantomData,
710 )
711 }
712}
713
714impl<'a, T, Name: ArgName> IntoIterator for &'a KwArgs<T, Name> {
715 type Item = (&'a Wtf8Buf, &'a T);
716 type IntoIter = indexmap::map::Iter<'a, Wtf8Buf, T>;
717
718 fn into_iter(self) -> Self::IntoIter {
719 self.0.iter()
720 }
721}
722
723impl<T, Name: ArgName> IntoIterator for KwArgs<T, Name> {
724 type Item = (Wtf8Buf, T);
725 type IntoIter = indexmap::map::IntoIter<Wtf8Buf, T>;
726
727 fn into_iter(self) -> Self::IntoIter {
728 self.0.into_iter()
729 }
730}
731
732impl<T, Name: ArgName> FromArgs for KwArgs<T, Name>
733where
734 T: TryFromObject,
735{
736 const PARAMS: Option<&'static [Param]> = Some(&[Param::var_keyword(Name::NAME)]);
737
738 fn from_args(vm: &VirtualMachine, args: &mut FuncArgs) -> Result<Self, ArgumentError> {
739 let mut kwargs = KwArgsMap::default();
740 for (name, value) in args.remaining_keywords() {
741 kwargs.insert(name, value.try_into_value(vm)?);
742 }
743 Ok(Self(kwargs, core::marker::PhantomData))
744 }
745}
746
747#[derive(Clone)]
756pub struct PosArgs<T = PyObjectRef, Name: ArgName = NameArgs>(
757 Vec<T>,
758 core::marker::PhantomData<Name>,
759);
760
761unsafe impl<T, Name: ArgName> Traverse for PosArgs<T, Name>
762where
763 T: Traverse,
764{
765 fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
766 self.0.traverse(tracer_fn)
767 }
768}
769
770impl<T> PosArgs<T> {
771 #[must_use]
772 pub const fn new(args: Vec<T>) -> Self {
773 Self(args, core::marker::PhantomData)
774 }
775}
776
777impl<T, Name: ArgName> PosArgs<T, Name> {
778 #[must_use]
779 pub const fn named(args: Vec<T>) -> Self {
780 Self(args, core::marker::PhantomData)
781 }
782
783 #[must_use]
784 pub fn into_vec(self) -> Vec<T> {
785 self.0
786 }
787
788 pub fn iter(&self) -> core::slice::Iter<'_, T> {
789 self.0.iter()
790 }
791}
792
793impl<T> From<Vec<T>> for PosArgs<T> {
794 fn from(v: Vec<T>) -> Self {
795 Self(v, core::marker::PhantomData)
796 }
797}
798
799impl From<()> for PosArgs<PyObjectRef> {
800 fn from(_args: ()) -> Self {
801 Self(Vec::new(), core::marker::PhantomData)
802 }
803}
804
805impl<T, Name: ArgName> AsRef<[T]> for PosArgs<T, Name> {
806 fn as_ref(&self) -> &[T] {
807 &self.0
808 }
809}
810
811impl<T: PyPayload, Name: ArgName> PosArgs<PyRef<T>, Name> {
812 pub fn into_tuple(self, vm: &VirtualMachine) -> PyTupleRef {
813 vm.ctx
814 .new_tuple(self.0.into_iter().map(Into::into).collect())
815 }
816}
817
818impl<T, Name: ArgName> FromArgs for PosArgs<T, Name>
819where
820 T: TryFromObject,
821{
822 const PARAMS: Option<&'static [Param]> = Some(&[Param::var_positional(Name::NAME)]);
823
824 fn from_args(vm: &VirtualMachine, args: &mut FuncArgs) -> Result<Self, ArgumentError> {
825 let mut varargs = Vec::new();
826 while let Some(value) = args.take_positional() {
827 varargs.push(value.try_into_value(vm)?);
828 }
829 Ok(Self(varargs, core::marker::PhantomData))
830 }
831}
832
833impl<T, Name: ArgName> IntoIterator for PosArgs<T, Name> {
834 type Item = T;
835 type IntoIter = alloc::vec::IntoIter<T>;
836
837 fn into_iter(self) -> Self::IntoIter {
838 self.0.into_iter()
839 }
840}
841
842impl<T> FromArgs for T
843where
844 T: TryFromObject,
845{
846 fn arity() -> RangeInclusive<usize> {
847 1..=1
848 }
849
850 fn from_args(vm: &VirtualMachine, args: &mut FuncArgs) -> Result<Self, ArgumentError> {
851 let value = args.take_positional().ok_or(ArgumentError::TooFewArgs)?;
852 Ok(value.try_into_value(vm)?)
853 }
854}
855
856#[derive(Debug, result_like::OptionLike, is_macro::Is)]
860pub enum OptionalArg<T = PyObjectRef> {
861 Present(T),
862 Missing,
863}
864
865unsafe impl<T> Traverse for OptionalArg<T>
866where
867 T: Traverse,
868{
869 fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
870 match self {
871 Self::Present(o) => o.traverse(tracer_fn),
872 Self::Missing => (),
873 }
874 }
875}
876
877#[derive(FromArgs)]
879pub struct PositionalIterable {
880 #[pyarg(positional, default, py_default = "()")]
881 pub iterable: OptionalArg<PyObjectRef>,
882}
883
884impl OptionalArg<PyObjectRef> {
885 pub fn unwrap_or_none(self, vm: &VirtualMachine) -> PyObjectRef {
886 self.unwrap_or_else(|| vm.ctx.none())
887 }
888}
889
890pub type OptionalOption<T = PyObjectRef> = OptionalArg<Option<T>>;
891
892impl<T> OptionalOption<T> {
893 #[inline]
894 pub fn flatten(self) -> Option<T> {
895 self.into_option().flatten()
896 }
897}
898
899impl<T> FromArgs for OptionalArg<T>
900where
901 T: TryFromObject,
902{
903 const PARAMS: Option<&'static [Param]> = Some(&[Param {
904 name: "",
905 kind: super::signature::ParamKind::PositionalOnly,
906 default: Some(super::signature::DefaultRepr::Unrepresentable),
907 }]);
908
909 fn arity() -> RangeInclusive<usize> {
910 0..=1
911 }
912
913 fn from_args(vm: &VirtualMachine, args: &mut FuncArgs) -> Result<Self, ArgumentError> {
914 let r = if let Some(value) = args.take_positional() {
915 Self::Present(value.try_into_value(vm)?)
916 } else {
917 Self::Missing
918 };
919 Ok(r)
920 }
921}
922
923impl FromArgs for () {
926 const PARAMS: Option<&'static [Param]> = Some(&[]);
927
928 fn from_args(_vm: &VirtualMachine, _args: &mut FuncArgs) -> Result<Self, ArgumentError> {
929 Ok(())
930 }
931}
932
933macro_rules! tuple_from_py_func_args {
940 ($($T:ident),+) => {
941 impl<$($T),+> FromArgs for ($($T,)+)
942 where
943 $($T: FromArgs),+
944 {
945 const PARAMS: Option<&'static [Param]> = {
946 if true $(&& $T::PARAMS.is_some())* {
947 Some(&[$(Param::flatten($T::PARAMS)),*])
948 } else {
949 None
950 }
951 };
952
953 fn arity() -> RangeInclusive<usize> {
954 let mut min = 0;
955 let mut max = 0;
956 $(
957 let (start, end) = $T::arity().into_inner();
958 min += start;
959 max += end;
960 )+
961 min..=max
962 }
963
964 fn from_args(vm: &VirtualMachine, args: &mut FuncArgs) -> Result<Self, ArgumentError> {
965 Ok(($($T::from_args(vm, args)?,)+))
966 }
967 }
968 };
969}
970
971tuple_from_py_func_args!(A);
975tuple_from_py_func_args!(A, B);
976tuple_from_py_func_args!(A, B, C);
977tuple_from_py_func_args!(A, B, C, D);
978tuple_from_py_func_args!(A, B, C, D, E);
979tuple_from_py_func_args!(A, B, C, D, E, F);
980tuple_from_py_func_args!(A, B, C, D, E, F, G);
981tuple_from_py_func_args!(A, B, C, D, E, F, G, H);