@@ -84,6 +84,14 @@ def select(obj, args, fields=None):
8484 if len (args ) == 1 :
8585
8686 arg = args [0 ]
87+
88+ if isinstance (arg , str ):
89+ # convert seleection_str to tuple of slices/coordinates
90+ if arg == "[...]" :
91+ args = (...,)
92+ else :
93+ args = _getSelectionList (obj_shape , arg )
94+
8795 if hasattr (arg , "shape" ):
8896 arg_shape = arg .shape
8997 else :
@@ -105,7 +113,6 @@ def select(obj, args, fields=None):
105113
106114 return Selection(shape, spaceid=sid)
107115 """
108-
109116 sel = SimpleSelection (obj_shape , args , fields = fields )
110117 return sel
111118
@@ -128,6 +135,161 @@ def _check_bool_args(s1, s2):
128135 raise ValueError ("selections have incompatible shapes" )
129136
130137
138+ def _getSelectElements (sel_str ):
139+ """helper method - return array of queries for each
140+ dimension"""
141+ if not isinstance (sel_str , str ):
142+ raise TypeError ("expected string arg" )
143+ if len (sel_str ) < 3 :
144+ raise ValueError ("selection string too short" )
145+ if sel_str [0 ] != '[' or sel_str [- 1 ] != ']' :
146+ raise ValueError ("unexpected selection string format" )
147+ sel_str = sel_str [1 :- 1 ] # strip brackets
148+
149+ query_array = []
150+ dim_query = []
151+ coord_list = False
152+ for ch in sel_str :
153+ if ch .isspace ():
154+ # ignore
155+ pass
156+ elif ch == "," :
157+ if coord_list :
158+ dim_query .append (ch )
159+ else :
160+ if len (dim_query ) == 0 :
161+ # empty dimension
162+ raise ValueError ("invalid query" )
163+ query_array .append ("" .join (dim_query ))
164+ dim_query = [] # reset
165+ elif ch == "[" :
166+ if coord_list :
167+ # can't have nested coordinates
168+ raise ValueError ("invalid query" )
169+ coord_list = True
170+ dim_query .append (ch )
171+ elif ch == "]" :
172+ if not coord_list :
173+ # close bracket with no open
174+ raise ValueError ("invalid query" )
175+ dim_query .append (ch )
176+ coord_list = False
177+ elif ch == ":" :
178+ if coord_list :
179+ # range not allowed in coord list
180+ raise ValueError ("invalid query" )
181+ dim_query .append (ch )
182+ else :
183+ dim_query .append (ch )
184+ if not dim_query :
185+ # empty dimension
186+ raise ValueError ("invalid query" )
187+ query_array .append ("" .join (dim_query ))
188+
189+ return query_array
190+
191+
192+ def _getSelectionList (shape , sel_str ):
193+ """Return tuple of slices and/or coordinate list for the given selection"""
194+ select_list = []
195+
196+ if sel_str is None or len (sel_str ) == 0 :
197+ """Return set of slices covering data space"""
198+ slices = []
199+ for extent in shape :
200+ s = slice (0 , extent , 1 )
201+ slices .append (s )
202+ return tuple (slices )
203+
204+ # convert selection to list by dimension
205+ elements = _getSelectElements (sel_str )
206+ rank = len (elements )
207+ if len (shape ) != rank :
208+ raise ValueError ("invalid rank for selection" )
209+ for dim in range (rank ):
210+ extent = shape [dim ]
211+ element = elements [dim ]
212+ is_list = isinstance (element , list )
213+ is_str = isinstance (element , str )
214+ if is_list or (is_str and element .startswith ("[" )):
215+ # list of coordinates
216+ if is_str :
217+ fields = element [1 :- 1 ].split ("," )
218+ else :
219+ fields = element
220+ coords = []
221+ for field in fields :
222+ if isinstance (field , str ) and not field :
223+ continue
224+ try :
225+ coord = int (field )
226+ except ValueError :
227+ raise ValueError (f"Invalid coordinate for dim { dim } " )
228+ if coord < 0 or coord >= extent :
229+ msg = f"out of range coordinate for dim { dim } , { coord } "
230+ msg += f"not in range: 0-{ extent - 1 } "
231+ raise ValueError (msg )
232+ coords .append (coord )
233+ select_list .append (coords )
234+ elif element == ":" :
235+ s = slice (0 , extent , 1 )
236+ select_list .append (s )
237+ elif is_str and element .find (":" ) >= 0 :
238+ fields = element .split (":" )
239+ if len (fields ) not in (2 , 3 ):
240+ raise ValueError (f"Invalid selection format for dim { dim } " )
241+ if len (fields [0 ]) == 0 :
242+ start = 0
243+ else :
244+ try :
245+ start = int (fields [0 ])
246+ except ValueError :
247+ raise ValueError (f"Invalid selection - start value for dim { dim } " )
248+ if start < 0 or start >= extent :
249+ msg = f"Invalid selection - start value out of range for dim { dim } "
250+ raise ValueError (msg )
251+ if len (fields [1 ]) == 0 :
252+ stop = extent
253+ else :
254+ try :
255+ stop = int (fields [1 ])
256+ except ValueError :
257+ raise ValueError (f"Invalid selection - stop value for dim { dim } " )
258+ if stop < 0 or stop > extent or stop <= start :
259+ msg = f"Invalid selection - stop value out of range for dim { dim } "
260+ raise ValueError (msg )
261+ if len (fields ) == 3 :
262+ # get step value
263+ if len (fields [2 ]) == 0 :
264+ step = 1
265+ else :
266+ try :
267+ step = int (fields [2 ])
268+ except ValueError :
269+ msg = f"Invalid selection - step value for dim { dim } "
270+ raise ValueError (msg )
271+ if step <= 0 :
272+ msg = f"Invalid selection - step value out of range for dim { dim } "
273+ raise ValueError (msg )
274+ else :
275+ step = 1
276+ s = slice (start , stop , step )
277+ select_list .append (s )
278+ else :
279+ # expect single coordinate value
280+ try :
281+ index = int (element )
282+ except ValueError :
283+ raise ValueError (f"Invalid selection - index value for dim { dim } " )
284+ if index < 0 or index >= extent :
285+ msg = f"Invalid selection - index value out of range for dim { dim } "
286+ raise ValueError (msg )
287+ s = slice (index , index + 1 , 1 )
288+ select_list .append (s )
289+ # end dimension loop
290+ return tuple (select_list )
291+
292+
131293def _points_to_paired (shape , points ):
132294 """Convert a list of point coordinates or a boolean array into the
133295 per-dimension tuple expected by SimpleSelection's fancy path.
@@ -794,6 +956,17 @@ def broadcast(self, target_shape):
794956 def __getitem__ (self , args ):
795957 raise NotImplementedError ("This class does not support indexing" )
796958
959+ def __eq__ (self , other ):
960+ if not isinstance (other , Selection ):
961+ return NotImplemented
962+ return all ((
963+ type (self ) is type (other ),
964+ self .shape == other .shape ,
965+ self .select_type == other .select_type ,
966+ self .fields == other .fields ,
967+ self .mshape == other .mshape ,
968+ ))
969+
797970 def __repr__ (self ):
798971 return f"Selection(shape:{ self ._shape } )"
799972
@@ -964,8 +1137,9 @@ def getSelectNpoints(self):
9641137 npoints *= m
9651138 return npoints
9661139
967- def getQueryParam (self ):
968- """ Get select param for use with HDF Rest API"""
1140+ @property
1141+ def query_string (self ):
1142+ """ The value of the 'select' query parameter for this selection, for use with the HDF REST API """
9691143 rank = len (self ._shape )
9701144 if rank == 0 :
9711145 return None
@@ -1047,6 +1221,16 @@ def broadcast(self, target_shape):
10471221 sel = [tuple ([sum (x ) for x in zip (offset , start )]), tshape , step , scalar ]
10481222 yield sel
10491223
1224+ def __eq__ (self , other ):
1225+ if not isinstance (other , SimpleSelection ):
1226+ return NotImplemented
1227+ return all ((
1228+ self .shape == other .shape ,
1229+ self .select_type == other .select_type ,
1230+ self .fields == other .fields ,
1231+ self .slices == other .slices ,
1232+ ))
1233+
10501234 def __repr__ (self ):
10511235 if self .fields :
10521236 fields = ", fields: " + str (self .fields )
@@ -1271,3 +1455,19 @@ def __init__(self, shape, *args, **kwds):
12711455 self ._select_type = H5S_SEL_ALL
12721456 else :
12731457 raise ValueError ("Illegal slicing argument for scalar dataspace" )
1458+
1459+ def __eq__ (self , other ):
1460+ if not isinstance (other , ScalarSelection ):
1461+ return NotImplemented
1462+ # mshape is not compared here: () vs (Ellipsis,) construction args produce
1463+ # different mshape (None vs ()) despite selecting the same scalar element,
1464+ # and select_type is H5S_SEL_ALL in both cases anyway.
1465+ return all ((
1466+ self .shape == other .shape ,
1467+ self .select_type == other .select_type ,
1468+ self .fields == other .fields ,
1469+ ))
1470+
1471+ @property
1472+ def query_string (self ):
1473+ return None
0 commit comments