This document gives an overview of how QMK has structured its python code. You should read this before working on any of the python code.
There are two places scripts live in QMK:
qmk_firmware/util. You should use
bin for any python scripts that utilize the
qmk wrapper. Scripts that are standalone and not run very often live in
We discourage putting anything into
bin that does not utilize the
qmk wrapper. If you think you have a good reason for doing so please talk to us about your use case.
Most of the QMK python modules can be found in
qmk_firmware/lib/python. This is the path that we append to
We have a module hierarchy under that path:
milc.py - The CLI library we use. Will be pulled out into its own module in the future.
qmk - Code associated with QMK
cli - Modules that will be imported for CLI commands.
errors.py - Errors that can be raised within QMK apps
keymap.py - Functions for working with keymaps
We have a CLI wrapper that you should utilize for any user facing scripts. We think it's pretty easy to use and it gives you a lot of nice things for free.
To use the wrapper simply place a module into
qmk_firmware/lib/python/qmk/cli, and create a symlink to
bin/qmk named after your module. Dashes in command names will be converted into dots so you can use hierarchy to manage commands.
qmk is run it checks to see how it was invoked. If it was invoked as
qmk the module name is take from
sys.argv. If it was invoked as
qmk-<module-name> then everything after the first dash is taken as the module name. Dashes and underscores are converted to dots, and then
qmk.cli is prepended before the module is imported.
The module uses
@cli.argument() decorators to define an entrypoint, which is where execution starts.
We have provided a QMK Hello World script you can use as an example. To run it simply run
qmk hello or
qmk-hello. The source code is listed below.
from milc import email@example.com('-n', '--name', default='World', help='Name to greet.')@cli.entrypoint('QMK Python Hello World.')def main(cli):cli.echo('Hello, %s!', cli.config.general.name)