aboutsummaryrefslogtreecommitdiff
path: root/lib/python/qmk/community_modules.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/python/qmk/community_modules.py')
-rw-r--r--lib/python/qmk/community_modules.py100
1 files changed, 100 insertions, 0 deletions
diff --git a/lib/python/qmk/community_modules.py b/lib/python/qmk/community_modules.py
new file mode 100644
index 0000000000..f7e96a6b93
--- /dev/null
+++ b/lib/python/qmk/community_modules.py
@@ -0,0 +1,100 @@
+import os
+
+from pathlib import Path
+from functools import lru_cache
+
+from milc.attrdict import AttrDict
+
+from qmk.json_schema import json_load, validate, merge_ordered_dicts
+from qmk.util import truthy
+from qmk.constants import QMK_FIRMWARE, QMK_USERSPACE, HAS_QMK_USERSPACE
+from qmk.path import under_qmk_firmware, under_qmk_userspace
+
+COMMUNITY_MODULE_JSON_FILENAME = 'qmk_module.json'
+
+
+class ModuleAPI(AttrDict):
+ def __init__(self, **kwargs):
+ super().__init__()
+ for key, value in kwargs.items():
+ self[key] = value
+
+
+@lru_cache(maxsize=1)
+def module_api_list():
+ module_definition_files = sorted(set(QMK_FIRMWARE.glob('data/constants/module_hooks/*.hjson')))
+ module_definition_jsons = [json_load(f) for f in module_definition_files]
+ module_definitions = merge_ordered_dicts(module_definition_jsons)
+ latest_module_version = module_definition_files[-1].stem
+ latest_module_version_parts = latest_module_version.split('.')
+
+ api_list = []
+ for name, mod in module_definitions.items():
+ api_list.append(ModuleAPI(
+ ret_type=mod['ret_type'],
+ name=name,
+ args=mod['args'],
+ call_params=mod.get('call_params', ''),
+ guard=mod.get('guard', None),
+ header=mod.get('header', None),
+ ))
+
+ return api_list, latest_module_version, latest_module_version_parts[0], latest_module_version_parts[1], latest_module_version_parts[2]
+
+
+def find_available_module_paths():
+ """Find all available modules.
+ """
+ search_dirs = []
+ if HAS_QMK_USERSPACE:
+ search_dirs.append(QMK_USERSPACE / 'modules')
+ search_dirs.append(QMK_FIRMWARE / 'modules')
+
+ modules = []
+ for search_dir in search_dirs:
+ for module_json_path in search_dir.rglob(COMMUNITY_MODULE_JSON_FILENAME):
+ modules.append(module_json_path.parent)
+ return modules
+
+
+def find_module_path(module):
+ """Find a module by name.
+ """
+ for module_path in find_available_module_paths():
+ # Ensure the module directory is under QMK Firmware or QMK Userspace
+ relative_path = under_qmk_firmware(module_path)
+ if not relative_path:
+ relative_path = under_qmk_userspace(module_path)
+ if not relative_path:
+ continue
+
+ lhs = str(relative_path.as_posix())[len('modules/'):]
+ rhs = str(Path(module).as_posix())
+
+ if relative_path and lhs == rhs:
+ return module_path
+ return None
+
+
+def load_module_json(module):
+ """Load a module JSON file.
+ """
+ module_path = find_module_path(module)
+ if not module_path:
+ raise FileNotFoundError(f'Module not found: {module}')
+
+ module_json = json_load(module_path / COMMUNITY_MODULE_JSON_FILENAME)
+
+ if not truthy(os.environ.get('SKIP_SCHEMA_VALIDATION'), False):
+ validate(module_json, 'qmk.community_module.v1')
+
+ module_json['module'] = module
+ module_json['module_path'] = module_path
+
+ return module_json
+
+
+def load_module_jsons(modules):
+ """Load the module JSON files, matching the specified order.
+ """
+ return list(map(load_module_json, modules))