docs: def invoke_function(self, ctx, name, arguments):
          """
          Invokes the given function
          :param ctx: the evaluation context
          :param name: the function name (case insensitive)
          :param arguments: the arguments to be passed to the function
          :return: the function return value
          """
          from temba_expressions import EvaluationError, conversions
  
          # find function with given name
          func = self.get_function(name)
          if func is None:
              raise EvaluationError("Undefined function: %s" % name)
  
          args, varargs, defaults = self._get_arg_spec(func)
  
          call_args = []
          passed_args = list(arguments)
  
          for arg in args:
              if arg == 'ctx':
                  call_args.append(ctx)
              elif passed_args:
                  call_args.append(passed_args.pop(0))
              elif arg in defaults:
                  call_args.append(defaults[arg])
              else:
                  raise EvaluationError("Too few arguments provided for function %s" % name)
  
          if varargs is not None:
              call_args.extend(passed_args)
              passed_args = []
  
          # any unused arguments?
          if passed_args:
              raise EvaluationError("Too many arguments provided for function %s" % name)
  
          try:
              return func(*call_args)
          except Exception as e:
              pretty_args = []
              for arg in arguments:
                  if isinstance(arg, str):
                      pretty = '"%s"' % arg
                  else:
                      try:
                          pretty = conversions.to_string(arg, ctx)
                      except EvaluationError:
                          pretty = str(arg)
                  pretty_args.append(pretty)
  
              raise EvaluationError("Error calling function %s with arguments %s" % (name, ', '.join(pretty_args)), e)