code: def execute_with_scope(expr, scope, aggcontext=None, clients=None, **kwargs):
      
      op = expr.op()
  
      # Call pre_execute, to allow clients to intercept the expression before
      # computing anything *and* before associating leaf nodes with data. This
      # allows clients to provide their own data for each leaf.
      if clients is None:
          clients = list(find_backends(expr))
  
      if aggcontext is None:
          aggcontext = agg_ctx.Summarize()
  
      pre_executed_scope = pre_execute(
          op, *clients, scope=scope, aggcontext=aggcontext, **kwargs
      )
      new_scope = toolz.merge(scope, pre_executed_scope)
      result = execute_until_in_scope(
          expr,
          new_scope,
          aggcontext=aggcontext,
          clients=clients,
          # XXX: we *explicitly* pass in scope and not new_scope here so that
          # post_execute sees the scope of execute_with_scope, not the scope of
          # execute_until_in_scope
          post_execute_=functools.partial(
              post_execute,
              scope=scope,
              aggcontext=aggcontext,
              clients=clients,
              **kwargs,
          ),
          **kwargs,
      )
  
      return result
docs: Execute an expression `expr`, with data provided in `scope`.
  
      Parameters
      ----------
      expr : ibis.expr.types.Expr
          The expression to execute.
      scope : collections.Mapping
          A dictionary mapping :class:`~ibis.expr.operations.Node` subclass
          instances to concrete data such as a pandas DataFrame.
      aggcontext : Optional[ibis.pandas.aggcontext.AggregationContext]
  
      Returns
      -------
      result : scalar, pd.Series, pd.DataFrame