in starlark/eval.go [1385:1515]
func setArgs(locals []Value, fn *Function, args Tuple, kwargs []Tuple) error {
// This is the general schema of a function:
//
// def f(p1, p2=dp2, p3=dp3, *args, k1, k2=dk2, k3, **kwargs)
//
// The p parameters are non-kwonly, and may be specified positionally.
// The k parameters are kwonly, and must be specified by name.
// The defaults tuple is (dp2, dp3, mandatory, dk2, mandatory).
//
// Arguments are processed as follows:
// - positional arguments are bound to a prefix of [p1, p2, p3].
// - surplus positional arguments are bound to *args.
// - keyword arguments are bound to any of {p1, p2, p3, k1, k2, k3};
// duplicate bindings are rejected.
// - surplus keyword arguments are bound to **kwargs.
// - defaults are bound to each parameter from p2 to k3 if no value was set.
// default values come from the tuple above.
// It is an error if the tuple entry for an unset parameter is 'mandatory'.
// Nullary function?
if fn.NumParams() == 0 {
if nactual := len(args) + len(kwargs); nactual > 0 {
return fmt.Errorf("function %s accepts no arguments (%d given)", fn.Name(), nactual)
}
return nil
}
cond := func(x bool, y, z interface{}) interface{} {
if x {
return y
}
return z
}
// nparams is the number of ordinary parameters (sans *args and **kwargs).
nparams := fn.NumParams()
var kwdict *Dict
if fn.HasKwargs() {
nparams--
kwdict = new(Dict)
locals[nparams] = kwdict
}
if fn.HasVarargs() {
nparams--
}
// nonkwonly is the number of non-kwonly parameters.
nonkwonly := nparams - fn.NumKwonlyParams()
// Too many positional args?
n := len(args)
if len(args) > nonkwonly {
if !fn.HasVarargs() {
return fmt.Errorf("function %s accepts %s%d positional argument%s (%d given)",
fn.Name(),
cond(len(fn.defaults) > fn.NumKwonlyParams(), "at most ", ""),
nonkwonly,
cond(nonkwonly == 1, "", "s"),
len(args))
}
n = nonkwonly
}
// Bind positional arguments to non-kwonly parameters.
for i := 0; i < n; i++ {
locals[i] = args[i]
}
// Bind surplus positional arguments to *args parameter.
if fn.HasVarargs() {
tuple := make(Tuple, len(args)-n)
for i := n; i < len(args); i++ {
tuple[i-n] = args[i]
}
locals[nparams] = tuple
}
// Bind keyword arguments to parameters.
paramIdents := fn.funcode.Locals[:nparams]
for _, pair := range kwargs {
k, v := pair[0].(String), pair[1]
if i := findParam(paramIdents, string(k)); i >= 0 {
if locals[i] != nil {
return fmt.Errorf("function %s got multiple values for parameter %s", fn.Name(), k)
}
locals[i] = v
continue
}
if kwdict == nil {
return fmt.Errorf("function %s got an unexpected keyword argument %s", fn.Name(), k)
}
oldlen := kwdict.Len()
kwdict.SetKey(k, v)
if kwdict.Len() == oldlen {
return fmt.Errorf("function %s got multiple values for parameter %s", fn.Name(), k)
}
}
// Are defaults required?
if n < nparams || fn.NumKwonlyParams() > 0 {
m := nparams - len(fn.defaults) // first default
// Report errors for missing required arguments.
var missing []string
var i int
for i = n; i < m; i++ {
if locals[i] == nil {
missing = append(missing, paramIdents[i].Name)
}
}
// Bind default values to parameters.
for ; i < nparams; i++ {
if locals[i] == nil {
dflt := fn.defaults[i-m]
if _, ok := dflt.(mandatory); ok {
missing = append(missing, paramIdents[i].Name)
continue
}
locals[i] = dflt
}
}
if missing != nil {
return fmt.Errorf("function %s missing %d argument%s (%s)",
fn.Name(), len(missing), cond(len(missing) > 1, "s", ""), strings.Join(missing, ", "))
}
}
return nil
}