qmk find: expand operator support (#24468)
				
					
				
			This commit is contained in:
		
							parent
							
								
									c7a04bd930
								
							
						
					
					
						commit
						65a8a5ff69
					
				| @ -153,20 +153,26 @@ qmk cd | |||||||
| 
 | 
 | ||||||
| This command allows for searching through keyboard/keymap targets, filtering by specific criteria. `info.json` and `rules.mk` files contribute to the search data, as well as keymap configurations, and the results can be filtered using "dotty" syntax matching the overall `info.json` file format. | This command allows for searching through keyboard/keymap targets, filtering by specific criteria. `info.json` and `rules.mk` files contribute to the search data, as well as keymap configurations, and the results can be filtered using "dotty" syntax matching the overall `info.json` file format. | ||||||
| 
 | 
 | ||||||
| For example, one could search for all keyboards using STM32F411: | For example, one could search for all keyboards powered by the STM32F411 microcontroller: | ||||||
| 
 | 
 | ||||||
| ``` | ``` | ||||||
| qmk find -f 'processor=STM32F411' | qmk find -f 'processor==STM32F411' | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ...and one can further constrain the list to keyboards using STM32F411 as well as rgb_matrix support: | The list can be further constrained by passing additional filter expressions: | ||||||
| 
 | 
 | ||||||
| ``` | ``` | ||||||
| qmk find -f 'processor=STM32F411' -f 'features.rgb_matrix=true' | qmk find -f 'processor==STM32F411' -f 'features.rgb_matrix==true' | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| The following filter expressions are also supported: | The following filter expressions are supported: | ||||||
| 
 | 
 | ||||||
|  |  - `key == value`: Match targets where `key` is equal to `value`. May include wildcards such as `*` and `?`. | ||||||
|  |  - `key != value`: Match targets where `key` is not `value`. May include wildcards such as `*` and `?`. | ||||||
|  |  - `key < value`: Match targets where `key` is a number less than `value`. | ||||||
|  |  - `key > value`: Match targets where `key` is a number greater than `value`. | ||||||
|  |  - `key <= value`: Match targets where `key` is a number less than or equal to `value`. | ||||||
|  |  - `key >= value`: Match targets where `key` is a number greater than or equal to `value`. | ||||||
|  - `exists(key)`: Match targets where `key` is present. |  - `exists(key)`: Match targets where `key` is present. | ||||||
|  - `absent(key)`: Match targets where `key` is not present. |  - `absent(key)`: Match targets where `key` is not present. | ||||||
|  - `contains(key, value)`: Match targets where `key` contains `value`. Can be used for strings, arrays and object keys. |  - `contains(key, value)`: Match targets where `key` contains `value`. Can be used for strings, arrays and object keys. | ||||||
| @ -175,7 +181,7 @@ The following filter expressions are also supported: | |||||||
| You can also list arbitrary values for each matched target with `--print`: | You can also list arbitrary values for each matched target with `--print`: | ||||||
| 
 | 
 | ||||||
| ``` | ``` | ||||||
| qmk find -f 'processor=STM32F411' -p 'keyboard_name' -p 'features.rgb_matrix' | qmk find -f 'processor==STM32F411' -p 'keyboard_name' -p 'features.rgb_matrix' | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| **Usage**: | **Usage**: | ||||||
|  | |||||||
| @ -239,11 +239,11 @@ def _filter_keymap_targets(target_list: List[KeyboardKeymapDesc], filters: List[ | |||||||
|         valid_targets = parallel_map(_load_keymap_info, target_list) |         valid_targets = parallel_map(_load_keymap_info, target_list) | ||||||
| 
 | 
 | ||||||
|         function_re = re.compile(r'^(?P<function>[a-zA-Z]+)\((?P<key>[a-zA-Z0-9_\.]+)(,\s*(?P<value>[^#]+))?\)$') |         function_re = re.compile(r'^(?P<function>[a-zA-Z]+)\((?P<key>[a-zA-Z0-9_\.]+)(,\s*(?P<value>[^#]+))?\)$') | ||||||
|         equals_re = re.compile(r'^(?P<key>[a-zA-Z0-9_\.]+)\s*=\s*(?P<value>[^#]+)$') |         comparison_re = re.compile(r'^(?P<key>[a-zA-Z0-9_\.]+)\s*(?P<op>[\<\>\!=]=|\<|\>)\s*(?P<value>[^#]+)$') | ||||||
| 
 | 
 | ||||||
|         for filter_expr in filters: |         for filter_expr in filters: | ||||||
|             function_match = function_re.match(filter_expr) |             function_match = function_re.match(filter_expr) | ||||||
|             equals_match = equals_re.match(filter_expr) |             comparison_match = comparison_re.match(filter_expr) | ||||||
| 
 | 
 | ||||||
|             if function_match is not None: |             if function_match is not None: | ||||||
|                 func_name = function_match.group('function').lower() |                 func_name = function_match.group('function').lower() | ||||||
| @ -259,23 +259,43 @@ def _filter_keymap_targets(target_list: List[KeyboardKeymapDesc], filters: List[ | |||||||
|                 value_str = f", {{fg_cyan}}{value}{{fg_reset}}" if value is not None else "" |                 value_str = f", {{fg_cyan}}{value}{{fg_reset}}" if value is not None else "" | ||||||
|                 cli.log.info(f'Filtering on condition: {{fg_green}}{func_name}{{fg_reset}}({{fg_cyan}}{key}{{fg_reset}}{value_str})...') |                 cli.log.info(f'Filtering on condition: {{fg_green}}{func_name}{{fg_reset}}({{fg_cyan}}{key}{{fg_reset}}{value_str})...') | ||||||
| 
 | 
 | ||||||
|             elif equals_match is not None: |             elif comparison_match is not None: | ||||||
|                 key = equals_match.group('key') |                 key = comparison_match.group('key') | ||||||
|                 value = equals_match.group('value') |                 op = comparison_match.group('op') | ||||||
|                 cli.log.info(f'Filtering on condition: {{fg_cyan}}{key}{{fg_reset}} == {{fg_cyan}}{value}{{fg_reset}}...') |                 value = comparison_match.group('value') | ||||||
|  |                 cli.log.info(f'Filtering on condition: {{fg_cyan}}{key}{{fg_reset}} {op} {{fg_cyan}}{value}{{fg_reset}}...') | ||||||
| 
 | 
 | ||||||
|                 def _make_filter(k, v): |                 def _make_filter(k, o, v): | ||||||
|                     expr = fnmatch.translate(v) |                     expr = fnmatch.translate(v) | ||||||
|                     rule = re.compile(f'^{expr}$', re.IGNORECASE) |                     rule = re.compile(f'^{expr}$', re.IGNORECASE) | ||||||
| 
 | 
 | ||||||
|                     def f(e: KeyboardKeymapDesc): |                     def f(e: KeyboardKeymapDesc): | ||||||
|                         lhs = e.dotty.get(k) |                         lhs = e.dotty.get(k) | ||||||
|                         lhs = str(False if lhs is None else lhs) |                         rhs = v | ||||||
|                         return rule.search(lhs) is not None | 
 | ||||||
|  |                         if o in ['<', '>', '<=', '>=']: | ||||||
|  |                             lhs = int(False if lhs is None else lhs) | ||||||
|  |                             rhs = int(rhs) | ||||||
|  | 
 | ||||||
|  |                             if o == '<': | ||||||
|  |                                 return lhs < rhs | ||||||
|  |                             elif o == '>': | ||||||
|  |                                 return lhs > rhs | ||||||
|  |                             elif o == '<=': | ||||||
|  |                                 return lhs <= rhs | ||||||
|  |                             elif o == '>=': | ||||||
|  |                                 return lhs >= rhs | ||||||
|  |                         else: | ||||||
|  |                             lhs = str(False if lhs is None else lhs) | ||||||
|  | 
 | ||||||
|  |                             if o == '!=': | ||||||
|  |                                 return rule.search(lhs) is None | ||||||
|  |                             elif o == '==': | ||||||
|  |                                 return rule.search(lhs) is not None | ||||||
| 
 | 
 | ||||||
|                     return f |                     return f | ||||||
| 
 | 
 | ||||||
|                 valid_targets = filter(_make_filter(key, value), valid_targets) |                 valid_targets = filter(_make_filter(key, op, value), valid_targets) | ||||||
|             else: |             else: | ||||||
|                 cli.log.warning(f'Unrecognized filter expression: {filter_expr}') |                 cli.log.warning(f'Unrecognized filter expression: {filter_expr}') | ||||||
|                 continue |                 continue | ||||||
|  | |||||||
| @ -390,7 +390,7 @@ def test_find_contains(): | |||||||
| def test_find_multiple_conditions(): | def test_find_multiple_conditions(): | ||||||
|     # this is intended to match at least 'crkbd/rev1' |     # this is intended to match at least 'crkbd/rev1' | ||||||
|     result = check_subcommand( |     result = check_subcommand( | ||||||
|         'find', '-f', 'exists(rgb_matrix.split_count)', '-f', 'contains(matrix_pins.cols, B1)', '-f', 'length(matrix_pins.cols, 6)', '-f', 'absent(eeprom.driver)', '-f', 'ws2812.pin=D3', '-p', 'rgb_matrix.split_count', '-p', 'matrix_pins.cols', '-p', |         'find', '-f', 'exists(rgb_matrix.split_count)', '-f', 'contains(matrix_pins.cols, B1)', '-f', 'length(matrix_pins.cols, 6)', '-f', 'absent(eeprom.driver)', '-f', 'ws2812.pin == D3', '-p', 'rgb_matrix.split_count', '-p', 'matrix_pins.cols', '-p', | ||||||
|         'eeprom.driver', '-p', 'ws2812.pin' |         'eeprom.driver', '-p', 'ws2812.pin' | ||||||
|     ) |     ) | ||||||
|     check_returncode(result) |     check_returncode(result) | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user