@@ -630,6 +630,51 @@ def visit_Call(self, node):
630630 return set (visitor .operands )
631631
632632
633+ def conserve_functions (
634+ expression : str ,
635+ operands_old : dict [str : blosc2 .ndarray | blosc2 .LazyExpr ],
636+ operands_new : dict [str : blosc2 .ndarray | blosc2 .LazyExpr ],
637+ ) -> tuple (str , dict [str : blosc2 .ndarray .NDArray | blosc2 .LazyExpr ]):
638+ operand_to_key = {id (v ): k for k , v in operands_new .items ()}
639+
640+ class OperandVisitor (ast .NodeVisitor ):
641+ def __init__ (self ):
642+ self .operandset = set ()
643+ self .operands = {}
644+ self .opcounter = 0
645+ self .function_names = set ()
646+
647+ def visit_Name (self , node ):
648+ if node .id == "np" :
649+ # Skip NumPy namespace (e.g. np.int8, which will be treated separately)
650+ return
651+ if node .id in self .function_names :
652+ # Skip function names
653+ return
654+ elif (node .id not in dtype_symbols ) and (node .id not in self .operandset ):
655+ k = operand_to_key [id (operands_old [node .id ])]
656+ newkey = f"o{ self .opcounter } "
657+ self .operands [newkey ] = operands_new [k ]
658+ node .id = newkey
659+ self .operandset .add (node .id )
660+ self .opcounter += 1
661+ else :
662+ pass
663+ self .generic_visit (node )
664+
665+ def visit_Call (self , node ):
666+ if isinstance (
667+ node .func , ast .Name
668+ ): # visits Call first, then Name, so don't increment operandcounter yet
669+ self .function_names .add (node .func .id )
670+ self .generic_visit (node )
671+
672+ tree = ast .parse (expression )
673+ visitor = OperandVisitor ()
674+ visitor .visit (tree )
675+ return ast .unparse (tree ), visitor .operands
676+
677+
633678class TransformNumpyCalls (ast .NodeTransformer ):
634679 def __init__ (self ):
635680 self .replacements = {}
@@ -2679,9 +2724,11 @@ def _new_expr(cls, expression, operands, guess, out=None, where=None, ne_args=No
26792724 _shape = new_expr .shape
26802725 if isinstance (new_expr , blosc2 .LazyExpr ):
26812726 # DO NOT restore the original expression and operands
2682- # new_expr.expression = new_expr.expression} #don't have to do anything
2683- new_expr .expression_tosave = _expression
2684- # new_expr.operands = new_expr.operands #don't have to do anything
2727+ # Instead rebase operands and restore only constructors
2728+ expression_ , operands_ = conserve_functions (expression , operands , new_expr .operands )
2729+ new_expr .expression = f"({ expression_ } )" # force parenthesis
2730+ new_expr .expression_tosave = expression
2731+ new_expr .operands = operands_
26852732 new_expr .operands_tosave = operands
26862733 else :
26872734 # An immediate evaluation happened (e.g. all operands are numpy arrays)
0 commit comments