Skip to main content

rustpython_vm/function/
argument.rs

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        // Build the final vec once instead of prepending (realloc + memmove).
19        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
36// A tuple of values that each implement `ToPyObject` represents a sequence of
37// arguments that can be bound and passed to a built-in function.
38macro_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// We currently allows only 6 unnamed positional arguments.
66// Please use `#[derive(FromArgs)]` and a struct for more complex argument parsing.
67// The number of limitation came from:
68// https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
69
70/// The `FuncArgs` struct is one of the most used structs when creating
71/// a rust function that can be called from python. It holds both positional
72/// arguments, as well as keyword arguments passed to the function.
73#[derive(Debug, Default, Clone, Traverse)]
74pub struct FuncArgs {
75    pub args: Vec<PyObjectRef>,
76    // sorted map, according to https://www.python.org/dev/peps/pep-0468/
77    pub kwargs: KwArgs,
78}
79
80/// Conversion from vector of python objects to function arguments.
81impl<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        // last `kwarg_names.len()` elements of args in order of appearance in the call signature
130        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    /// Create FuncArgs from a vectorcall-style argument slice (PEP 590).
145    /// `args[..nargs]` are positional, and if `kwnames` is provided,
146    /// the last `kwnames.len()` entries in `args[nargs..]` are keyword values.
147    /// Convert borrowed vectorcall args to FuncArgs (clones all values).
148    #[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                    // `PyStr`, not `PyUtf8Str`: a surrogate key is a valid str and
165                    // must survive as WTF-8 rather than panic.
166                    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    /// Convert owned vectorcall args to FuncArgs (moves values, no clone).
183    #[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        // reserve (not reserve_exact): incoming vectors are usually built with
218        // exact capacity, so exact growth would realloc on every prepend.
219        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    /// Binds these arguments to their respective values.
284    ///
285    /// If there is an insufficient number of arguments, there are leftover
286    /// arguments after performing the binding, or if an argument is not of
287    /// the expected type, a TypeError is raised.
288    ///
289    /// If the given `FromArgs` includes any conversions, exceptions raised
290    /// during the conversion will halt the binding and return the error.
291    pub fn bind<T: FromArgs>(self, vm: &VirtualMachine) -> PyResult<T> {
292        self.bind_for(vm, Callee::default())
293    }
294
295    /// Binds these arguments the way [`bind`](Self::bind) does, for a call whose
296    /// function a failure can describe.
297    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        // A message describes the parameters the function declares, and the
304        // instance a method is called on is not one of them.
305        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    /// The same as [`check_kwargs_empty`](Self::check_kwargs_empty), for a call
327    /// whose function the message can name.
328    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/// What a message says about the function whose arguments are being bound.
342///
343/// A binding that happens somewhere the name isn't known leaves it off, the way
344/// `_PyArg_Parser.fname` is NULL. A message describes the parameters the
345/// function declares, so a method's instance argument counts as neither an
346/// expected parameter nor a given argument, the way `descrobject.c` reports
347/// `nargs - 1`.
348#[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    /// A function a message can name.
362    #[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    /// The type a slot builds or initializes, named the way a message names it.
371    #[must_use]
372    pub fn for_type(class: &crate::Py<crate::builtins::PyType>) -> Self {
373        Self::named(class.slots.name)
374    }
375
376    /// The same, for the type a slot was written for.
377    #[must_use]
378    pub fn of<T: crate::PyPayload>(vm: &VirtualMachine) -> Self {
379        Self::for_type(T::class(&vm.ctx))
380    }
381
382    /// Marks a call whose leading argument fills the method's instance parameter.
383    #[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    /// How many leading arguments answer for the instance rather than for a
390    /// parameter the method declares.
391    const fn instance_args(self) -> usize {
392        self.instance_arg as usize
393    }
394
395    /// _PyArg_CheckPositional
396    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    /// The branch of _PyArg_UnpackKeywords that names a keyword it didn't expect.
407    fn unexpected_keyword(self, keyword: &str, vm: &VirtualMachine) -> PyBaseExceptionRef {
408        vm.new_type_error(unexpected_keyword_message(self.name, keyword))
409    }
410
411    /// The branch of _PyArg_UnpackKeywords that names a parameter it didn't get.
412    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
422/// The name a message uses. `tp_name` carries the module along with the type,
423/// and a message names only the type itself.
424fn short_name(name: &str) -> &str {
425    name.rsplit_once('.').map_or(name, |(_, name)| name)
426}
427
428/// The name a message opens with and the parentheses that follow it, given what
429/// to call a function whose name isn't known.
430fn 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
437/// _PyArg_CheckPositional
438pub(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
461/// The branch of _PyArg_UnpackKeywords that names a keyword it didn't expect.
462pub(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
467/// The branch of _PyArg_UnpackKeywords that names a parameter it didn't get.
468pub(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
473/// An error encountered while binding arguments to the parameters of a Python
474/// function call.
475pub enum ArgumentError {
476    /// The call provided fewer positional arguments than the function requires.
477    TooFewArgs,
478    /// The call provided more positional arguments than the function accepts.
479    TooManyArgs,
480    /// The function doesn't accept a keyword argument with the given name.
481    InvalidKeywordArgument(String),
482    /// The function requires an argument for the named parameter, at the given
483    /// 1-based position, but the call didn't pass one.
484    MissingRequiredArgument { name: String, pos: usize },
485    /// An exception was raised while binding arguments to the function
486    /// parameters.
487    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
514/// Implemented by any type that can be accepted as a parameter to a built-in
515/// function.
516///
517pub trait FromArgs: Sized {
518    /// The range of positional arguments permitted by the function signature.
519    ///
520    /// Returns an empty range if not applicable.
521    #[must_use]
522    fn arity() -> RangeInclusive<usize> {
523        0..=0
524    }
525
526    /// Parameters this type contributes to a text signature.
527    ///
528    /// `None`: the argument is one positional-only parameter, named by the
529    /// function argument. `Some(&[])`: the argument contributes nothing.
530    /// `Some(params)`: the type supplies those parameters and the argument name
531    /// is ignored.
532    const PARAMS: Option<&'static [Param]> = None;
533
534    /// Extracts this item from the next argument(s).
535    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
543/// Signature default of a field marked `optional`.
544///
545/// Only [`Option`] (`None`) and [`OptionalArg`] (`<unrepresentable>`) are valid.
546pub 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
573/// A map of keyword arguments to their values.
574///
575/// A built-in function with a `KwArgs` parameter is analogous to a Python
576/// function with `**kwargs`. All remaining keyword arguments are extracted
577/// (and hence the function will permit an arbitrary number of them).
578///
579/// `KwArgs` optionally accepts a generic type parameter to allow type checks
580/// or conversions of each argument.
581///
582/// Note:
583///
584/// KwArgs is only for functions that accept arbitrary keyword arguments. For
585/// functions that accept only *specific* named arguments, a rust struct with
586/// an appropriate FromArgs implementation must be created.
587// Keys are stored as `Wtf8Buf`, not `String`, so that a lone-surrogate keyword
588// name coming through `f(**d)` is preserved instead of being rejected (see
589// issue #8228). `PyStr` is WTF-8 backed, and CPython only requires that a
590// keyword key be a `str`, not that it be valid UTF-8.
591pub 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
627/// The map behind [`KwArgs`].
628///
629/// The hasher is zero-sized rather than the randomly seeded default: a
630/// `KwArgs` is built for every call, including the far more common
631/// keyword-less one, and seeding reads a thread-local. Keyword names come
632/// from the program text, so per-process hash randomization buys nothing.
633pub 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    // `String` keys accepted `&str` lookups for free via `Borrow<str>`; `Wtf8Buf`
673    // borrows only as `Wtf8`, so these inherent methods restore the `&str` interface
674    // via the zero-cost `Wtf8::new` cast, keeping every call site unchanged.
675    #[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
703// Accept any key that converts into `Wtf8Buf` (notably `String`), so existing
704// call sites that build kwargs from string literals keep compiling unchanged.
705impl<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/// A list of positional argument values.
748///
749/// A built-in function with a `PosArgs` parameter is analogous to a Python
750/// function with `*args`. All remaining positional arguments are extracted
751/// (and hence the function will permit an arbitrary number of them).
752///
753/// `PosArgs` optionally accepts a generic type parameter to allow type checks
754/// or conversions of each argument.
755#[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/// An argument that may or may not be provided by the caller.
857///
858/// This style of argument is not possible in pure Python.
859#[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/// One positional-only iterable, defaulting to an empty tuple.
878#[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
923// For functions that accept no arguments. Implemented explicitly instead of via
924// macro below to avoid unused warnings.
925impl 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
933// A tuple of types that each implement `FromArgs` represents a sequence of
934// arguments that can be bound and passed to a built-in function.
935//
936// Technically, a tuple can contain tuples, which can contain tuples, and so on,
937// so this actually represents a tree of values to be bound from arguments, but
938// in practice this is only used for the top-level parameters.
939macro_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
971// Implement `FromArgs` for up to 7-tuples, allowing built-in functions to bind
972// up to 7 top-level parameters (note that `PosArgs`, `KwArgs`, nested tuples, etc.
973// count as 1, so this should actually be more than enough).
974tuple_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);