def execute_node_without_scope(node, **kwargs): raise com.UnboundExpressionError( ( 'Node of type {!r} has no data bound to it. ' 'You probably tried to execute an expression without a data ' 'source.' ).format(type(node).__name__) )
def execute_bottom_up(expr, scope, aggcontext=None, post_execute_=None, clients=None, **kwargs): """Execute `expr` bottom-up. Parameters ---------- expr : ibis.expr.types.Expr scope : Mapping[ibis.expr.operations.Node, object] aggcontext : Optional[ibis.pandas.aggcontext.AggregationContext] kwargs : Dict[str, object] Returns ------- result : Mapping[ ibis.expr.operations.Node, Union[pandas.Series, pandas.DataFrame, scalar_types] ] A mapping from node to the computed result of that Node """ assert post_execute_ is not None, 'post_execute_ is None' op = expr.op() # if we're in scope then return the scope, this will then be passed back # into execute_bottom_up, which will then terminate if op in scope: return scope elif isinstance(op, ops.Literal): # special case literals to avoid the overhead of dispatching # execute_node return { op: execute_literal(op, op.value, expr.type(), aggcontext=aggcontext, **kwargs) } # figure out what arguments we're able to compute on based on the # expressions inputs. things like expressions, None, and scalar types are # computable whereas ``list``s are not computable_args = [arg for arg in op.inputs if is_computable_input(arg)] # recursively compute each node's arguments until we've changed type scopes = [ execute_bottom_up( arg, scope, aggcontext=aggcontext, post_execute_=post_execute_, clients=clients, **kwargs, ) if hasattr(arg, 'op') else { arg: arg } for arg in computable_args ] # if we're unable to find data then raise an exception if not scopes: raise com.UnboundExpressionError( 'Unable to find data for expression:\n{}'.format(repr(expr))) # there should be exactly one dictionary per computable argument assert len(computable_args) == len(scopes) new_scope = toolz.merge(scopes) # pass our computed arguments to this node's execute_node implementation data = [ new_scope[arg.op()] if hasattr(arg, 'op') else arg for arg in computable_args ] result = execute_node( op, *data, scope=scope, aggcontext=aggcontext, clients=clients, **kwargs, ) computed = post_execute_(op, result) return {op: computed}