@@ -250,6 +250,16 @@ def _get_result(expression, chunk_operands, ne_args, where=None, indices=None, _
250250# (will be evaluated via the array interface)
251251additional_funcs = sorted ((numpy_funcs | numpy_ufuncs ) - set (blosc2_funcs ))
252252functions = blosc2_funcs + additional_funcs
253+ _constructor_call_patterns = {name : re .compile (rf"\b{ re .escape (name )} \s*\(" ) for name in constructors }
254+
255+
256+ def _has_constructor_call (expression : str , constructor : str ) -> bool :
257+ return _constructor_call_patterns [constructor ].search (expression ) is not None
258+
259+
260+ def _find_constructor_call (expression : str , constructor : str ) -> re .Match | None :
261+ return _constructor_call_patterns [constructor ].search (expression )
262+
253263
254264relational_ops = ["==" , "!=" , "<" , "<=" , ">" , ">=" ]
255265logical_ops = ["&" , "|" , "^" , "~" ]
@@ -2856,7 +2866,7 @@ def shape(self):
28562866 return None
28572867
28582868 # Operands shape can change, so we always need to recompute this
2859- if any (constructor in self .expression for constructor in constructors ):
2869+ if any (_has_constructor_call ( self .expression , constructor ) for constructor in constructors ):
28602870 # might have an expression with pure constructors
28612871 opshapes = {k : v if not hasattr (v , "shape" ) else v .shape for k , v in self .operands .items ()}
28622872 _shape = infer_shape (self .expression , opshapes ) # infer shape, includes constructors
@@ -3225,8 +3235,11 @@ def find_args(expr):
32253235 return expr [idx :i ], i + 1
32263236 raise ValueError ("Unbalanced parenthesis in expression" )
32273237
3228- # Find the index of the first parenthesis after the constructor
3229- idx = expression .find (f"{ constructor } " )
3238+ # Find the index of the first constructor call.
3239+ match = _find_constructor_call (expression , constructor )
3240+ if match is None :
3241+ raise ValueError (f"Constructor '{ constructor } ' not found in expression: { expression } " )
3242+ idx = match .start ()
32303243 # Find the arguments of the constructor function
32313244 try :
32323245 args , idx2 = find_args (expression [idx + len (constructor ) :])
@@ -3294,16 +3307,16 @@ def _compute_expr(self, item, kwargs):
32943307
32953308 return chunked_eval (lazy_expr .expression , lazy_expr .operands , item , ** kwargs )
32963309
3297- if any (constructor in self .expression for constructor in constructors ):
3310+ if any (_has_constructor_call ( self .expression , constructor ) for constructor in constructors ):
32983311 expression = self .expression
32993312 newexpr = expression
33003313 newops = self .operands .copy ()
33013314 # We have constructors in the expression (probably coming from a string lazyexpr)
33023315 # Let's replace the constructors with the actual NDArray objects
33033316 for constructor in constructors :
3304- if constructor not in newexpr :
3317+ if not _has_constructor_call ( newexpr , constructor ) :
33053318 continue
3306- while constructor in newexpr :
3319+ while _has_constructor_call ( newexpr , constructor ) :
33073320 # Get the constructor function and replace it by an NDArray object in the operands
33083321 # Find the constructor call and its arguments
33093322 value , constexpr = self ._eval_constructor (newexpr , constructor , newops )
@@ -4082,7 +4095,7 @@ def lazyexpr(
40824095 operands = seek_operands (operand_set , local_dict , global_dict , _frame_depth = _frame_depth )
40834096 else :
40844097 # No operands found in the expression. Maybe a constructor?
4085- constructor = any (constructor in expression for constructor in constructors )
4098+ constructor = any (_has_constructor_call ( expression , constructor ) for constructor in constructors )
40864099 if not constructor :
40874100 raise ValueError ("No operands nor constructors found in the expression" )
40884101 # _new_expr will take care of the constructor, but needs an empty dict in operands
0 commit comments