diff options
| author | QMK Bot <[email protected]> | 2023-03-16 20:36:32 +0000 | 
|---|---|---|
| committer | QMK Bot <[email protected]> | 2023-03-16 20:36:32 +0000 | 
| commit | 79ae14ae0accfdb7496821ed61adbcd4c99fe28a (patch) | |
| tree | 603b6d539b565205d569abf40118e3f339dc4c3f /lib/python/qmk/search.py | |
| parent | 23313c59763a65042f711606a67ae9262cb584ca (diff) | |
| parent | 1b3f7fcf7d3d441623e065c0be7821c5c5d87c75 (diff) | |
| download | qmk_firmware-79ae14ae0accfdb7496821ed61adbcd4c99fe28a.tar.gz qmk_firmware-79ae14ae0accfdb7496821ed61adbcd4c99fe28a.zip | |
Merge remote-tracking branch 'origin/master' into develop
Diffstat (limited to 'lib/python/qmk/search.py')
| -rw-r--r-- | lib/python/qmk/search.py | 99 | 
1 files changed, 99 insertions, 0 deletions
| diff --git a/lib/python/qmk/search.py b/lib/python/qmk/search.py new file mode 100644 index 0000000000..af48900e6b --- /dev/null +++ b/lib/python/qmk/search.py @@ -0,0 +1,99 @@ +"""Functions for searching through QMK keyboards and keymaps. +""" +import contextlib +import fnmatch +import logging +import multiprocessing +import re +from dotty_dict import dotty +from milc import cli + +from qmk.info import keymap_json +import qmk.keyboard +import qmk.keymap + + +def _set_log_level(level): +    cli.acquire_lock() +    old = cli.log_level +    cli.log_level = level +    cli.log.setLevel(level) +    logging.root.setLevel(level) +    cli.release_lock() +    return old + + +def ignore_logging(): +    old = _set_log_level(logging.CRITICAL) +    yield +    _set_log_level(old) + + +def _all_keymaps(keyboard): +    with ignore_logging(): +        return (keyboard, qmk.keymap.list_keymaps(keyboard)) + + +def _keymap_exists(keyboard, keymap): +    with ignore_logging(): +        return keyboard if qmk.keymap.locate_keymap(keyboard, keymap) is not None else None + + +def _load_keymap_info(keyboard, keymap): +    with ignore_logging(): +        return (keyboard, keymap, keymap_json(keyboard, keymap)) + + +def search_keymap_targets(keymap='default', filters=[]): +    targets = [] + +    with multiprocessing.Pool() as pool: +        cli.log.info(f'Retrieving list of keyboards with keymap "{keymap}"...') +        target_list = [] +        if keymap == 'all': +            kb_to_kms = pool.map(_all_keymaps, qmk.keyboard.list_keyboards()) +            for targets in kb_to_kms: +                keyboard = targets[0] +                keymaps = targets[1] +                target_list.extend([(keyboard, keymap) for keymap in keymaps]) +        else: +            target_list = [(kb, keymap) for kb in filter(lambda kb: kb is not None, pool.starmap(_keymap_exists, [(kb, keymap) for kb in qmk.keyboard.list_keyboards()]))] + +        if len(filters) == 0: +            targets = target_list +        else: +            cli.log.info('Parsing data for all matching keyboard/keymap combinations...') +            valid_keymaps = [(e[0], e[1], dotty(e[2])) for e in pool.starmap(_load_keymap_info, target_list)] + +            equals_re = re.compile(r'^(?P<key>[a-zA-Z0-9_\.]+)\s*=\s*(?P<value>[^#]+)$') +            exists_re = re.compile(r'^exists\((?P<key>[a-zA-Z0-9_\.]+)\)$') +            for filter_txt in filters: +                f = equals_re.match(filter_txt) +                if f is not None: +                    key = f.group('key') +                    value = f.group('value') +                    cli.log.info(f'Filtering on condition ("{key}" == "{value}")...') + +                    def _make_filter(k, v): +                        expr = fnmatch.translate(v) +                        rule = re.compile(f'^{expr}$', re.IGNORECASE) + +                        def f(e): +                            lhs = e[2].get(k) +                            lhs = str(False if lhs is None else lhs) +                            return rule.search(lhs) is not None + +                        return f + +                    valid_keymaps = filter(_make_filter(key, value), valid_keymaps) + +                f = exists_re.match(filter_txt) +                if f is not None: +                    key = f.group('key') +                    cli.log.info(f'Filtering on condition (exists: "{key}")...') +                    valid_keymaps = filter(lambda e: e[2].get(key) is not None, valid_keymaps) + +            targets = [(e[0], e[1]) for e in valid_keymaps] + +    return targets | 
