@@ -541,7 +541,8 @@ def compute_smaller_slice(larger_shape, smaller_shape, larger_slice):
541
541
542
542
# Define the patterns for validation
543
543
validation_patterns = [
544
- r"[\;\[\:]" , # Flow control characters
544
+ # r"[\;\[\:]", # Flow control characters
545
+ r"[\;]" , # Flow control characters
545
546
r"(^|[^\w])__[\w]+__($|[^\w])" , # Dunder methods
546
547
r"\.\b(?!real|imag|(\d*[eE]?[+-]?\d+)|(\d*[eE]?[+-]?\d+j)|\d*j\b|(sum|prod|min|max|std|mean|var|any|all|where)"
547
548
r"\s*\([^)]*\)|[a-zA-Z_]\w*\s*\([^)]*\))" , # Attribute patterns
@@ -592,7 +593,7 @@ def validate_expr(expr: str) -> None:
592
593
raise ValueError (f"'{ expr } ' is not a valid expression." )
593
594
594
595
# Check for invalid characters not covered by the tokenizer
595
- invalid_chars = re .compile (r"[^\w\s+\-*/%().,=<>!&|~^]" )
596
+ invalid_chars = re .compile (r"[^\w\s+\-*/%()[] .,=<>!&|~^]" )
596
597
if invalid_chars .search (skip_quotes ) is not None :
597
598
invalid_chars = invalid_chars .findall (skip_quotes )
598
599
raise ValueError (f"Expression { expr } contains invalid characters: { invalid_chars } " )
@@ -744,6 +745,38 @@ def visit_Call(self, node):
744
745
return newexpression , newoperands
745
746
746
747
748
+ def convert_to_slice (expression ):
749
+ """
750
+ Assumes all operands are of the form o...
751
+ Parameters
752
+ ----------
753
+ expression: str
754
+
755
+ Returns
756
+ -------
757
+ new_expr : str
758
+ """
759
+
760
+ new_expr = ""
761
+ skip_to_char = 0
762
+ for i , expr_i in enumerate (expression ):
763
+ if i < skip_to_char :
764
+ continue
765
+ if expr_i == "[" :
766
+ k = expression [i :].find ("]" ) # start checking from after [
767
+ slice_convert = expression [i : i + k + 1 ] # include [ and ]
768
+ slicer = eval (f"np.s_{ slice_convert } " )
769
+ slicer = (slicer ,) if isinstance (slicer , slice ) else slicer # standardise to tuple
770
+ if any (isinstance (el , str ) for el in slicer ): # handle fields
771
+ raise ValueError ("Cannot handle fields for slicing lazy expressions." )
772
+ slicer = str (slicer )
773
+ new_expr += ".slice(" + slicer + ")"
774
+ skip_to_char = i + k + 1
775
+ else :
776
+ new_expr += expr_i
777
+ return new_expr
778
+
779
+
747
780
class TransformNumpyCalls (ast .NodeTransformer ):
748
781
def __init__ (self ):
749
782
self .replacements = {}
@@ -2780,6 +2813,7 @@ def save(self, urlpath=None, **kwargs):
2780
2813
def _new_expr (cls , expression , operands , guess , out = None , where = None , ne_args = None ):
2781
2814
# Validate the expression
2782
2815
validate_expr (expression )
2816
+ expression = convert_to_slice (expression )
2783
2817
if guess :
2784
2818
# The expression has been validated, so we can evaluate it
2785
2819
# in guessing mode to avoid computing reductions
0 commit comments