keyboard stuff
1import os
2
3from pathlib import Path
4from functools import lru_cache
5
6from milc.attrdict import AttrDict
7
8from qmk.json_schema import json_load, validate, merge_ordered_dicts
9from qmk.util import truthy
10from qmk.constants import QMK_FIRMWARE, QMK_USERSPACE, HAS_QMK_USERSPACE
11from qmk.path import under_qmk_firmware, under_qmk_userspace
12
13COMMUNITY_MODULE_JSON_FILENAME = 'qmk_module.json'
14
15
16class ModuleAPI(AttrDict):
17 def __init__(self, **kwargs):
18 super().__init__()
19 for key, value in kwargs.items():
20 self[key] = value
21
22
23@lru_cache(maxsize=1)
24def module_api_list():
25 module_definition_files = sorted(set(QMK_FIRMWARE.glob('data/constants/module_hooks/*.hjson')))
26 module_definition_jsons = [json_load(f) for f in module_definition_files]
27 module_definitions = merge_ordered_dicts(module_definition_jsons)
28 latest_module_version = module_definition_files[-1].stem
29 latest_module_version_parts = latest_module_version.split('.')
30
31 api_list = []
32 for name, mod in module_definitions.items():
33 api_list.append(ModuleAPI(
34 ret_type=mod['ret_type'],
35 name=name,
36 args=mod['args'],
37 call_params=mod.get('call_params', ''),
38 guard=mod.get('guard', None),
39 header=mod.get('header', None),
40 ))
41
42 return api_list, latest_module_version, latest_module_version_parts[0], latest_module_version_parts[1], latest_module_version_parts[2]
43
44
45def find_available_module_paths():
46 """Find all available modules.
47 """
48 search_dirs = []
49 if HAS_QMK_USERSPACE:
50 search_dirs.append(QMK_USERSPACE / 'modules')
51 search_dirs.append(QMK_FIRMWARE / 'modules')
52
53 modules = []
54 for search_dir in search_dirs:
55 for module_json_path in search_dir.rglob(COMMUNITY_MODULE_JSON_FILENAME):
56 modules.append(module_json_path.parent)
57 return modules
58
59
60def find_module_path(module):
61 """Find a module by name.
62 """
63 for module_path in find_available_module_paths():
64 # Ensure the module directory is under QMK Firmware or QMK Userspace
65 relative_path = under_qmk_firmware(module_path)
66 if not relative_path:
67 relative_path = under_qmk_userspace(module_path)
68 if not relative_path:
69 continue
70
71 lhs = str(relative_path.as_posix())[len('modules/'):]
72 rhs = str(Path(module).as_posix())
73
74 if relative_path and lhs == rhs:
75 return module_path
76 return None
77
78
79def load_module_json(module):
80 """Load a module JSON file.
81 """
82 module_path = find_module_path(module)
83 if not module_path:
84 raise FileNotFoundError(f'Module not found: {module}')
85
86 module_json = json_load(module_path / COMMUNITY_MODULE_JSON_FILENAME)
87
88 if not truthy(os.environ.get('SKIP_SCHEMA_VALIDATION'), False):
89 validate(module_json, 'qmk.community_module.v1')
90
91 module_json['module'] = module
92 module_json['module_path'] = module_path
93
94 return module_json
95
96
97def load_module_jsons(modules):
98 """Load the module JSON files, matching the specified order.
99 """
100 return list(map(load_module_json, modules))