diff options
| author | Nick Brassel <nick@tzarc.org> | 2023-10-17 09:43:50 +1100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-10-17 09:43:50 +1100 |
| commit | f6c70c40af502923594e76bf079f602ed88a2341 (patch) | |
| tree | da27fe0d9c8c4fa24e39afd82d1a479b02e06e60 /lib/python/qmk | |
| parent | 81a3aa025cda52732e847af8db256cb132605ce0 (diff) | |
Allow for disabling of parallel processing of qmk find and `qmk mass-compile`. (#22160)
Co-authored-by: Duncan Sutherland <dunk2k_2000@hotmail.com>
Diffstat (limited to 'lib/python/qmk')
| -rw-r--r-- | lib/python/qmk/cli/find.py | 2 | ||||
| -rwxr-xr-x | lib/python/qmk/cli/mass_compile.py | 4 | ||||
| -rw-r--r-- | lib/python/qmk/search.py | 30 | ||||
| -rw-r--r-- | lib/python/qmk/util.py | 56 |
4 files changed, 73 insertions, 19 deletions
diff --git a/lib/python/qmk/cli/find.py b/lib/python/qmk/cli/find.py index 2836eb8a54..f2135bbc16 100644 --- a/lib/python/qmk/cli/find.py +++ b/lib/python/qmk/cli/find.py | |||
| @@ -23,7 +23,7 @@ def find(cli): | |||
| 23 | if len(cli.args.filter) == 0 and len(cli.args.print) > 0: | 23 | if len(cli.args.filter) == 0 and len(cli.args.print) > 0: |
| 24 | cli.log.warning('No filters supplied -- keymaps not parsed, unable to print requested values.') | 24 | cli.log.warning('No filters supplied -- keymaps not parsed, unable to print requested values.') |
| 25 | 25 | ||
| 26 | targets = search_keymap_targets(cli.args.keymap, cli.args.filter, cli.args.print) | 26 | targets = search_keymap_targets([('all', cli.config.find.keymap)], cli.args.filter, cli.args.print) |
| 27 | for keyboard, keymap, print_vals in targets: | 27 | for keyboard, keymap, print_vals in targets: |
| 28 | print(f'{keyboard}:{keymap}') | 28 | print(f'{keyboard}:{keymap}') |
| 29 | 29 | ||
diff --git a/lib/python/qmk/cli/mass_compile.py b/lib/python/qmk/cli/mass_compile.py index 1227f435e7..06e6e411a7 100755 --- a/lib/python/qmk/cli/mass_compile.py +++ b/lib/python/qmk/cli/mass_compile.py | |||
| @@ -97,6 +97,6 @@ def mass_compile(cli): | |||
| 97 | if len(cli.args.builds) > 0: | 97 | if len(cli.args.builds) > 0: |
| 98 | targets = search_make_targets(cli.args.builds, cli.args.filter) | 98 | targets = search_make_targets(cli.args.builds, cli.args.filter) |
| 99 | else: | 99 | else: |
| 100 | targets = search_keymap_targets(cli.args.keymap, cli.args.filter) | 100 | targets = search_keymap_targets([('all', cli.config.mass_compile.keymap)], cli.args.filter) |
| 101 | 101 | ||
| 102 | return mass_compile_targets(targets, cli.args.clean, cli.args.dry_run, cli.args.no_temp, cli.args.parallel, cli.args.env) | 102 | return mass_compile_targets(targets, cli.args.clean, cli.args.dry_run, cli.config.mass_compile.no_temp, cli.config.mass_compile.parallel, cli.args.env) |
diff --git a/lib/python/qmk/search.py b/lib/python/qmk/search.py index 0b5d489218..a74450ca87 100644 --- a/lib/python/qmk/search.py +++ b/lib/python/qmk/search.py | |||
| @@ -4,12 +4,12 @@ import contextlib | |||
| 4 | import functools | 4 | import functools |
| 5 | import fnmatch | 5 | import fnmatch |
| 6 | import logging | 6 | import logging |
| 7 | import multiprocessing | ||
| 8 | import re | 7 | import re |
| 9 | from typing import List, Tuple | 8 | from typing import List, Tuple |
| 10 | from dotty_dict import dotty | 9 | from dotty_dict import dotty |
| 11 | from milc import cli | 10 | from milc import cli |
| 12 | 11 | ||
| 12 | from qmk.util import parallel_map | ||
| 13 | from qmk.info import keymap_json | 13 | from qmk.info import keymap_json |
| 14 | import qmk.keyboard | 14 | import qmk.keyboard |
| 15 | import qmk.keymap | 15 | import qmk.keymap |
| @@ -78,17 +78,16 @@ def _expand_keymap_target(keyboard: str, keymap: str, all_keyboards: List[str] = | |||
| 78 | all_keyboards = qmk.keyboard.list_keyboards() | 78 | all_keyboards = qmk.keyboard.list_keyboards() |
| 79 | 79 | ||
| 80 | if keyboard == 'all': | 80 | if keyboard == 'all': |
| 81 | with multiprocessing.Pool() as pool: | 81 | if keymap == 'all': |
| 82 | if keymap == 'all': | 82 | cli.log.info('Retrieving list of all keyboards and keymaps...') |
| 83 | cli.log.info('Retrieving list of all keyboards and keymaps...') | 83 | targets = [] |
| 84 | targets = [] | 84 | for kb in parallel_map(_all_keymaps, all_keyboards): |
| 85 | for kb in pool.imap_unordered(_all_keymaps, all_keyboards): | 85 | targets.extend(kb) |
| 86 | targets.extend(kb) | 86 | return targets |
| 87 | return targets | 87 | else: |
| 88 | else: | 88 | cli.log.info(f'Retrieving list of keyboards with keymap "{keymap}"...') |
| 89 | cli.log.info(f'Retrieving list of keyboards with keymap "{keymap}"...') | 89 | keyboard_filter = functools.partial(_keymap_exists, keymap=keymap) |
| 90 | keyboard_filter = functools.partial(_keymap_exists, keymap=keymap) | 90 | return [(kb, keymap) for kb in filter(lambda e: e is not None, parallel_map(keyboard_filter, all_keyboards))] |
| 91 | return [(kb, keymap) for kb in filter(lambda e: e is not None, pool.imap_unordered(keyboard_filter, all_keyboards))] | ||
| 92 | else: | 91 | else: |
| 93 | if keymap == 'all': | 92 | if keymap == 'all': |
| 94 | keyboard = qmk.keyboard.resolve_keyboard(keyboard) | 93 | keyboard = qmk.keyboard.resolve_keyboard(keyboard) |
| @@ -117,8 +116,7 @@ def _filter_keymap_targets(target_list: List[Tuple[str, str]], filters: List[str | |||
| 117 | targets = [(kb, km, {}) for kb, km in target_list] | 116 | targets = [(kb, km, {}) for kb, km in target_list] |
| 118 | else: | 117 | else: |
| 119 | cli.log.info('Parsing data for all matching keyboard/keymap combinations...') | 118 | cli.log.info('Parsing data for all matching keyboard/keymap combinations...') |
| 120 | with multiprocessing.Pool() as pool: | 119 | valid_keymaps = [(e[0], e[1], dotty(e[2])) for e in parallel_map(_load_keymap_info, target_list)] |
| 121 | valid_keymaps = [(e[0], e[1], dotty(e[2])) for e in pool.imap_unordered(_load_keymap_info, target_list)] | ||
| 122 | 120 | ||
| 123 | function_re = re.compile(r'^(?P<function>[a-zA-Z]+)\((?P<key>[a-zA-Z0-9_\.]+)(,\s*(?P<value>[^#]+))?\)$') | 121 | function_re = re.compile(r'^(?P<function>[a-zA-Z]+)\((?P<key>[a-zA-Z0-9_\.]+)(,\s*(?P<value>[^#]+))?\)$') |
| 124 | equals_re = re.compile(r'^(?P<key>[a-zA-Z0-9_\.]+)\s*=\s*(?P<value>[^#]+)$') | 122 | equals_re = re.compile(r'^(?P<key>[a-zA-Z0-9_\.]+)\s*=\s*(?P<value>[^#]+)$') |
| @@ -179,10 +177,10 @@ def _filter_keymap_targets(target_list: List[Tuple[str, str]], filters: List[str | |||
| 179 | return targets | 177 | return targets |
| 180 | 178 | ||
| 181 | 179 | ||
| 182 | def search_keymap_targets(keymap='default', filters: List[str] = [], print_vals: List[str] = []) -> List[Tuple[str, str, List[Tuple[str, str]]]]: | 180 | def search_keymap_targets(targets: List[Tuple[str, str]] = [('all', 'default')], filters: List[str] = [], print_vals: List[str] = []) -> List[Tuple[str, str, List[Tuple[str, str]]]]: |
| 183 | """Search for build targets matching the supplied criteria. | 181 | """Search for build targets matching the supplied criteria. |
| 184 | """ | 182 | """ |
| 185 | return list(sorted(_filter_keymap_targets(expand_keymap_targets([('all', keymap)]), filters, print_vals), key=lambda e: (e[0], e[1]))) | 183 | return list(sorted(_filter_keymap_targets(expand_keymap_targets(targets), filters, print_vals), key=lambda e: (e[0], e[1]))) |
| 186 | 184 | ||
| 187 | 185 | ||
| 188 | def search_make_targets(targets: List[str], filters: List[str] = [], print_vals: List[str] = []) -> List[Tuple[str, str, List[Tuple[str, str]]]]: | 186 | def search_make_targets(targets: List[str], filters: List[str] = [], print_vals: List[str] = []) -> List[Tuple[str, str, List[Tuple[str, str]]]]: |
diff --git a/lib/python/qmk/util.py b/lib/python/qmk/util.py new file mode 100644 index 0000000000..db7debd578 --- /dev/null +++ b/lib/python/qmk/util.py | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | """Utility functions. | ||
| 2 | """ | ||
| 3 | import contextlib | ||
| 4 | import multiprocessing | ||
| 5 | |||
| 6 | from milc import cli | ||
| 7 | |||
| 8 | |||
| 9 | @contextlib.contextmanager | ||
| 10 | def parallelize(): | ||
| 11 | """Returns a function that can be used in place of a map() call. | ||
| 12 | |||
| 13 | Attempts to use `mpire`, falling back to `multiprocessing` if it's not | ||
| 14 | available. If parallelization is not requested, returns the original map() | ||
| 15 | function. | ||
| 16 | """ | ||
| 17 | |||
| 18 | # Work out if we've already got a config value for parallel searching | ||
| 19 | if cli.config.user.parallel_search is None: | ||
| 20 | parallel_search = True | ||
| 21 | else: | ||
| 22 | parallel_search = cli.config.user.parallel_search | ||
| 23 | |||
| 24 | # Non-parallel searches use `map()` | ||
| 25 | if not parallel_search: | ||
| 26 | yield map | ||
| 27 | return | ||
| 28 | |||
| 29 | # Prefer mpire's `WorkerPool` if it's available | ||
| 30 | with contextlib.suppress(ImportError): | ||
| 31 | from mpire import WorkerPool | ||
| 32 | from mpire.utils import make_single_arguments | ||
| 33 | with WorkerPool() as pool: | ||
| 34 | |||
| 35 | def _worker(func, *args): | ||
| 36 | # Ensure we don't unpack tuples -- mpire's `WorkerPool` tries to do so normally so we tell it not to. | ||
| 37 | for r in pool.imap_unordered(func, make_single_arguments(*args, generator=False), progress_bar=True): | ||
| 38 | yield r | ||
| 39 | |||
| 40 | yield _worker | ||
| 41 | return | ||
| 42 | |||
| 43 | # Otherwise fall back to multiprocessing's `Pool` | ||
| 44 | with multiprocessing.Pool() as pool: | ||
| 45 | yield pool.imap_unordered | ||
| 46 | |||
| 47 | |||
| 48 | def parallel_map(*args, **kwargs): | ||
| 49 | """Effectively runs `map()` but executes it in parallel if necessary. | ||
| 50 | """ | ||
| 51 | with parallelize() as map_fn: | ||
| 52 | # This needs to be enclosed in a `list()` as some implementations return | ||
| 53 | # a generator function, which means the scope of the pool is closed off | ||
| 54 | # before the results are returned. Returning a list ensures results are | ||
| 55 | # materialised before any worker pool is shut down. | ||
| 56 | return list(map_fn(*args, **kwargs)) | ||
