def test_search_range(happi_client: Client, valve: OphydItem): happi_client.add_item(valve) # Search between two points res = happi_client.search_range('z', start=0, end=500) assert len(res) == 2 # Search between two points, nothing found res = happi_client.search_range('z', start=10000, end=500000) assert not res # Search without an endpoint res = happi_client.search_range('z', start=0) assert len(res) == 2 # Search invalid range with pytest.raises(ValueError): happi_client.search_range('z', start=1000, end=5)
def search_parser( client: happi.Client, use_glob: bool, search_criteria: List[str], ) -> List[happi.SearchResult]: """ Parse the user's search criteria and return the search results. ``search_criteria`` must be a list of key=value strings. If key is omitted, it will be assumed to be "name". Parameters ---------- client : Client The happi client that we'll be doing searches in. use_glob : bool True if we're using glob matching, False if we're using regex matching. search_criteria : list of str The user's search selection from the command line. """ # Get search criteria into dictionary for use by client client_args = {} range_list = [] regex_list = [] range_found = False with client.retain_cache_context(): for user_arg in search_criteria: if '=' in user_arg: criteria, value = user_arg.split('=', 1) else: criteria = 'name' value = user_arg if criteria in client_args: logger.error( 'Received duplicate search criteria %s=%r (was %r)', criteria, value, client_args[criteria]) raise click.Abort() if is_a_range(value): start, stop = value.split(',') start = float(start) stop = float(stop) if start < stop: new_range_list = client.search_range(criteria, start, stop) else: logger.error('Invalid range, make sure start < stop') raise click.Abort() if not range_found: # if first range, just replace range_found = True range_list = new_range_list else: # subsequent ranges, only take intersection range_list = set(new_range_list) & set(range_list) if not range_list: # we have searched via a range query. At this point # no matches, or intesection is empty. abort early logger.error("No items found") return [] continue elif is_number(value): if float(value) == int(float(value)): # value is an int, allow the float version (optional .0) logger.debug(f'looking for int value: {value}') value = f'^{int(float(value))}(\\.0+$)?$' # don't translate from glob client_args[criteria] = value continue else: value = str(float(value)) else: logger.debug('Value %s interpreted as string', value) if use_glob: client_args[criteria] = fnmatch.translate(value) else: # already using regex client_args[criteria] = value regex_list = client.search_regex(**client_args) # Gather final results final_results = [] if regex_list and not range_list: # only matched with one search_regex() final_results = regex_list elif range_list and not regex_list: # only matched with search_range() final_results = range_list elif range_list and regex_list: # find the intersection between regex_list and range_list final_results = set(range_list) & set(regex_list) else: logger.debug('No regex or range items found') if not final_results: logger.error('No items found') return final_results