Use pathlib everywhere we can (#7872)

* Use pathlib everywhere we can

* Update lib/python/qmk/path.py

Co-Authored-By: Erovia <Erovia@users.noreply.github.com>

* Update lib/python/qmk/path.py

Co-Authored-By: Erovia <Erovia@users.noreply.github.com>

* Improvements based on @erovia's feedback

* rework qmk compile and qmk flash to use pathlib

* style

* Remove the subcommand_name argument from find_keyboard_keymap()

Co-authored-by: Erovia <Erovia@users.noreply.github.com>
This commit is contained in:
skullydazed 2020-02-17 11:42:11 -08:00 committed by GitHub
parent 58724f8dcb
commit c66930445f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 215 additions and 171 deletions

View File

@ -273,7 +273,7 @@ class MILC(object):
self._inside_context_manager = False self._inside_context_manager = False
self.ansi = ansi_colors self.ansi = ansi_colors
self.arg_only = [] self.arg_only = []
self.config = None self.config = self.config_source = None
self.config_file = None self.config_file = None
self.default_arguments = {} self.default_arguments = {}
self.version = 'unknown' self.version = 'unknown'
@ -473,6 +473,7 @@ class MILC(object):
""" """
self.acquire_lock() self.acquire_lock()
self.config = Configuration() self.config = Configuration()
self.config_source = Configuration()
self.config_file = self.find_config_file() self.config_file = self.find_config_file()
if self.config_file and self.config_file.exists(): if self.config_file and self.config_file.exists():
@ -498,6 +499,7 @@ class MILC(object):
value = int(value) value = int(value)
self.config[section][option] = value self.config[section][option] = value
self.config_source[section][option] = 'config_file'
self.release_lock() self.release_lock()
@ -530,12 +532,14 @@ class MILC(object):
arg_value = getattr(self.args, argument) arg_value = getattr(self.args, argument)
if arg_value is not None: if arg_value is not None:
self.config[section][argument] = arg_value self.config[section][argument] = arg_value
self.config_source[section][argument] = 'argument'
else: else:
if argument not in self.config[entrypoint_name]: if argument not in self.config[entrypoint_name]:
# Check if the argument exist for this section # Check if the argument exist for this section
arg = getattr(self.args, argument) arg = getattr(self.args, argument)
if arg is not None: if arg is not None:
self.config[section][argument] = arg self.config[section][argument] = arg
self.config_source[section][argument] = 'argument'
self.release_lock() self.release_lock()

View File

@ -3,16 +3,12 @@
You can compile a keymap already in the repo or using a QMK Configurator export. You can compile a keymap already in the repo or using a QMK Configurator export.
""" """
import subprocess import subprocess
import os
from argparse import FileType from argparse import FileType
from milc import cli from milc import cli
from qmk.commands import create_make_command
from qmk.commands import parse_configurator_json
from qmk.commands import compile_configurator_json
import qmk.keymap
import qmk.path import qmk.path
from qmk.commands import compile_configurator_json, create_make_command, find_keyboard_keymap, parse_configurator_json
@cli.argument('filename', nargs='?', arg_only=True, type=FileType('r'), help='The configurator export to compile') @cli.argument('filename', nargs='?', arg_only=True, type=FileType('r'), help='The configurator export to compile')
@ -24,99 +20,28 @@ def compile(cli):
If a Configurator export is supplied this command will create a new keymap, overwriting an existing keymap if one exists. If a Configurator export is supplied this command will create a new keymap, overwriting an existing keymap if one exists.
FIXME(skullydazed): add code to check and warn if the keymap already exists If a keyboard and keymap are provided this command will build a firmware based on that.
If --keyboard and --keymap are provided this command will build a firmware based on that.
""" """
# Set CWD as directory command was issued from
cwd = os.environ['ORIG_CWD']
qmk_path = os.getcwd()
current_folder = os.path.basename(cwd)
# Initialize boolean to check for being in a keyboard directory and initialize keyboard string
in_keyboard = False
in_layout = False
keyboard = ""
keymap = ""
user_keymap = ""
user_keyboard = ""
# Set path for '/keyboards/' directory
keyboards_path = os.path.join(qmk_path, "keyboards")
layouts_path = os.path.join(qmk_path, "layouts")
# If below 'keyboards' and not in 'keyboards' or 'keymaps', get current keyboard name
if cwd.startswith(keyboards_path):
if current_folder != "keyboards" and current_folder != "keymaps":
if os.path.basename(os.path.abspath(os.path.join(cwd, ".."))) == "keymaps":
# If in a keymap folder, set relative path, get everything before /keymaps, and the keymap name
relative_path = cwd[len(keyboards_path):][1:]
keyboard = str(relative_path).split("/keymaps", 1)[0]
keymap = str(relative_path.rsplit("/", 1)[-1])
else:
keyboard = str(cwd[len(keyboards_path):])[1:]
in_keyboard = True
# If in layouts dir
if cwd.startswith(layouts_path):
if current_folder != "layouts":
in_layout = True
# If user keyboard/keymap or compile keyboard/keymap are supplied, assign those
if cli.config.compile.keyboard:
user_keyboard = cli.config.compile.keyboard
if cli.config.compile.keymap and not in_layout:
user_keymap = cli.config.compile.keymap
if cli.args.filename: if cli.args.filename:
# Parse the configurator json # If a configurator JSON was provided skip straight to compiling it
# FIXME(skullydazed): add code to check and warn if the keymap already exists when compiling a json keymap.
user_keymap = parse_configurator_json(cli.args.filename) user_keymap = parse_configurator_json(cli.args.filename)
# Generate the keymap
keymap_path = qmk.path.keymap(user_keymap['keyboard']) keymap_path = qmk.path.keymap(user_keymap['keyboard'])
cli.log.info('Creating {fg_cyan}%s{style_reset_all} keymap in {fg_cyan}%s', user_keymap['keymap'], keymap_path)
# Compile the keymap
command = compile_configurator_json(user_keymap) command = compile_configurator_json(user_keymap)
cli.log.info('Wrote keymap to {fg_cyan}%s/%s/keymap.c', keymap_path, user_keymap['keymap']) cli.log.info('Wrote keymap to {fg_cyan}%s/%s/keymap.c', keymap_path, user_keymap['keymap'])
elif user_keyboard and user_keymap:
# Generate the make command for a specific keyboard/keymap.
command = create_make_command(user_keyboard, user_keymap)
elif in_keyboard:
keyboard = user_keyboard if user_keyboard else keyboard
keymap = user_keymap if user_keymap else keymap
if not os.path.exists(os.path.join(keyboards_path, keyboard, "rules.mk")):
cli.log.error('This directory does not contain a rules.mk file. Change directory or supply --keyboard with optional --keymap')
return False
# Get path for keyboard directory
keymap_path = qmk.path.keymap(keyboard)
# Check for global keymap config first
if keymap:
command = create_make_command(keyboard, keymap)
else:
# If no default keymap exists and none provided
cli.log.error('This directory does not contain a keymap. Set one with `qmk config` or supply `--keymap` ')
return False
elif in_layout:
if user_keyboard:
keymap = current_folder
command = create_make_command(user_keyboard, keymap)
else:
cli.log.error('You must supply a keyboard to compile a layout keymap. Set one with `qmk config` or supply `--keyboard` ')
return False
else: else:
cli.log.error('You must supply a configurator export, both `--keyboard` and `--keymap`, or be in a directory for a keyboard or keymap.') # Perform the action the user specified
return False user_keyboard, user_keymap = find_keyboard_keymap()
if user_keyboard and user_keymap:
# Generate the make command for a specific keyboard/keymap.
command = create_make_command(user_keyboard, user_keymap)
else:
cli.log.error('You must supply a configurator export, both `--keyboard` and `--keymap`, or be in a directory for a keyboard or keymap.')
cli.echo('usage: qmk compile [-h] [-b] [-kb KEYBOARD] [-km KEYMAP] [filename]')
return False
cli.log.info('Compiling keymap with {fg_cyan}%s\n\n', ' '.join(command)) cli.log.info('Compiling keymap with {fg_cyan}%s\n\n', ' '.join(command))
subprocess.run(command) subprocess.run(command)

View File

@ -8,7 +8,7 @@ from argparse import FileType
import qmk.path import qmk.path
from milc import cli from milc import cli
from qmk.commands import compile_configurator_json, create_make_command, parse_configurator_json from qmk.commands import compile_configurator_json, create_make_command, find_keyboard_keymap, parse_configurator_json
def print_bootloader_help(): def print_bootloader_help():
@ -45,39 +45,31 @@ def flash(cli):
If bootloader is omitted, the one according to the rules.mk will be used. If bootloader is omitted, the one according to the rules.mk will be used.
""" """
command = []
if cli.args.bootloaders: if cli.args.bootloaders:
# Provide usage and list bootloaders # Provide usage and list bootloaders
cli.echo('usage: qmk flash [-h] [-b] [-kb KEYBOARD] [-km KEYMAP] [-bl BOOTLOADER] [filename]') cli.echo('usage: qmk flash [-h] [-b] [-kb KEYBOARD] [-km KEYMAP] [-bl BOOTLOADER] [filename]')
print_bootloader_help() print_bootloader_help()
return False return False
elif cli.config.flash.keymap and not cli.config.flash.keyboard: if cli.args.filename:
# If only a keymap was given but no keyboard, suggest listing keyboards # Handle compiling a configurator JSON
cli.echo('usage: qmk flash [-h] [-b] [-kb KEYBOARD] [-km KEYMAP] [-bl BOOTLOADER] [filename]')
cli.log.error('run \'qmk list_keyboards\' to find out the supported keyboards')
return False
elif cli.args.filename:
# Get keymap path to log info
user_keymap = parse_configurator_json(cli.args.filename) user_keymap = parse_configurator_json(cli.args.filename)
keymap_path = qmk.path.keymap(user_keymap['keyboard']) keymap_path = qmk.path.keymap(user_keymap['keyboard'])
cli.log.info('Creating {fg_cyan}%s{style_reset_all} keymap in {fg_cyan}%s', user_keymap['keymap'], keymap_path)
# Convert the JSON into a C file and write it to disk.
command = compile_configurator_json(user_keymap, cli.args.bootloader) command = compile_configurator_json(user_keymap, cli.args.bootloader)
cli.log.info('Wrote keymap to {fg_cyan}%s/%s/keymap.c', keymap_path, user_keymap['keymap']) cli.log.info('Wrote keymap to {fg_cyan}%s/%s/keymap.c', keymap_path, user_keymap['keymap'])
elif cli.config.flash.keyboard and cli.config.flash.keymap:
# Generate the make command for a specific keyboard/keymap.
command = create_make_command(cli.config.flash.keyboard, cli.config.flash.keymap, cli.args.bootloader)
else: else:
cli.echo('usage: qmk flash [-h] [-b] [-kb KEYBOARD] [-km KEYMAP] [-bl BOOTLOADER] [filename]') # Perform the action the user specified
cli.log.error('You must supply a configurator export or both `--keyboard` and `--keymap`. You can also specify a bootloader with --bootloader. Use --bootloaders to list the available bootloaders.') user_keyboard, user_keymap = find_keyboard_keymap()
return False if user_keyboard and user_keymap:
# Generate the make command for a specific keyboard/keymap.
command = create_make_command(user_keyboard, user_keymap, cli.args.bootloader)
else:
cli.log.error('You must supply a configurator export or both `--keyboard` and `--keymap`.')
cli.echo('usage: qmk flash [-h] [-b] [-kb KEYBOARD] [-km KEYMAP] [-bl BOOTLOADER] [filename]')
return False
cli.log.info('Flashing keymap with {fg_cyan}%s\n\n', ' '.join(command)) cli.log.info('Flashing keymap with {fg_cyan}%s\n\n', ' '.join(command))
subprocess.run(command) subprocess.run(command)

View File

@ -1,14 +1,15 @@
"""Generate a keymap.c from a configurator export. """Generate a keymap.c from a configurator export.
""" """
import json import json
import os from pathlib import Path
from milc import cli from milc import cli
import qmk.keymap import qmk.keymap
import qmk.path
@cli.argument('-o', '--output', arg_only=True, help='File to write to') @cli.argument('-o', '--output', arg_only=True, type=Path, help='File to write to')
@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages") @cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
@cli.argument('filename', arg_only=True, help='Configurator JSON file') @cli.argument('filename', arg_only=True, help='Configurator JSON file')
@cli.subcommand('Creates a keymap.c from a QMK Configurator export.') @cli.subcommand('Creates a keymap.c from a QMK Configurator export.')
@ -17,13 +18,17 @@ def json_keymap(cli):
This command uses the `qmk.keymap` module to generate a keymap.c from a configurator export. The generated keymap is written to stdout, or to a file if -o is provided. This command uses the `qmk.keymap` module to generate a keymap.c from a configurator export. The generated keymap is written to stdout, or to a file if -o is provided.
""" """
cli.args.filename = qmk.path.normpath(cli.args.filename)
# Error checking # Error checking
if cli.args.filename == ('-'): if not cli.args.filename.exists():
cli.log.error('Reading from STDIN is not (yet) supported.') cli.log.error('JSON file does not exist!')
cli.print_usage() cli.print_usage()
exit(1) exit(1)
if not os.path.exists(qmk.path.normpath(cli.args.filename)):
cli.log.error('JSON file does not exist!') if str(cli.args.filename) == '-':
# TODO(skullydazed/anyone): Read file contents from STDIN
cli.log.error('Reading from STDIN is not (yet) supported.')
cli.print_usage() cli.print_usage()
exit(1) exit(1)
@ -32,21 +37,17 @@ def json_keymap(cli):
cli.args.output = None cli.args.output = None
# Parse the configurator json # Parse the configurator json
with open(qmk.path.normpath(cli.args.filename), 'r') as fd: with cli.args.filename.open('r') as fd:
user_keymap = json.load(fd) user_keymap = json.load(fd)
# Generate the keymap # Generate the keymap
keymap_c = qmk.keymap.generate(user_keymap['keyboard'], user_keymap['layout'], user_keymap['layers']) keymap_c = qmk.keymap.generate(user_keymap['keyboard'], user_keymap['layout'], user_keymap['layers'])
if cli.args.output: if cli.args.output:
output_dir = os.path.dirname(cli.args.output) cli.args.output.parent.mkdir(parents=True, exist_ok=True)
if cli.args.output.exists():
if not os.path.exists(output_dir): cli.args.output.replace(cli.args.output.name + '.bak')
os.makedirs(output_dir) cli.args.output.write_text(keymap_c)
output_file = qmk.path.normpath(cli.args.output)
with open(output_file, 'w') as keymap_fd:
keymap_fd.write(keymap_c)
if not cli.args.quiet: if not cli.args.quiet:
cli.log.info('Wrote keymap to %s.', cli.args.output) cli.log.info('Wrote keymap to %s.', cli.args.output)

View File

@ -1,5 +1,6 @@
"""List the keyboards currently defined within QMK """List the keyboards currently defined within QMK
""" """
# We avoid pathlib here because this is performance critical code.
import os import os
import glob import glob

View File

@ -1,8 +1,9 @@
"""This script automates the copying of the default keymap into your own keymap. """This script automates the copying of the default keymap into your own keymap.
""" """
import os
import shutil import shutil
from pathlib import Path
import qmk.path
from milc import cli from milc import cli
@ -17,24 +18,27 @@ def new_keymap(cli):
keymap = cli.config.new_keymap.keymap if cli.config.new_keymap.keymap else input("Keymap Name: ") keymap = cli.config.new_keymap.keymap if cli.config.new_keymap.keymap else input("Keymap Name: ")
# generate keymap paths # generate keymap paths
kb_path = os.path.join(os.getcwd(), "keyboards", keyboard) kb_path = Path('keyboards') / keyboard
keymap_path_default = os.path.join(kb_path, "keymaps/default") keymap_path = qmk.path.keymap(keyboard)
keymap_path = os.path.join(kb_path, "keymaps/%s" % keymap) keymap_path_default = keymap_path / 'default'
keymap_path_new = keymap_path / keymap
# check directories # check directories
if not os.path.exists(kb_path): if not kb_path.exists():
cli.log.error('Keyboard %s does not exist!', kb_path) cli.log.error('Keyboard %s does not exist!', kb_path)
exit(1) exit(1)
if not os.path.exists(keymap_path_default):
if not keymap_path_default.exists():
cli.log.error('Keyboard default %s does not exist!', keymap_path_default) cli.log.error('Keyboard default %s does not exist!', keymap_path_default)
exit(1) exit(1)
if os.path.exists(keymap_path):
cli.log.error('Keymap %s already exists!', keymap_path) if keymap_path_new.exists():
cli.log.error('Keymap %s already exists!', keymap_path_new)
exit(1) exit(1)
# create user directory with default keymap files # create user directory with default keymap files
shutil.copytree(keymap_path_default, keymap_path, symlinks=True) shutil.copytree(str(keymap_path_default), str(keymap_path_new), symlinks=True)
# end message to user # end message to user
cli.log.info("%s keymap directory created in: %s", keymap, keymap_path) cli.log.info("%s keymap directory created in: %s", keymap, keymap_path_new)
cli.log.info("Compile a firmware with your new keymap by typing: \n" + "qmk compile -kb %s -km %s", keyboard, keymap) cli.log.info("Compile a firmware with your new keymap by typing: \n\n\tqmk compile -kb %s -km %s\n", keyboard, keymap)

View File

@ -1,13 +1,19 @@
"""Functions that build make commands """Helper functions for commands.
""" """
import json import json
from pathlib import Path
from milc import cli
import qmk.keymap import qmk.keymap
from qmk.path import is_keyboard, is_keymap_dir, under_qmk_firmware
def create_make_command(keyboard, keymap, target=None): def create_make_command(keyboard, keymap, target=None):
"""Create a make compile command """Create a make compile command
Args: Args:
keyboard keyboard
The path of the keyboard, for example 'plank' The path of the keyboard, for example 'plank'
@ -18,24 +24,22 @@ def create_make_command(keyboard, keymap, target=None):
Usually a bootloader. Usually a bootloader.
Returns: Returns:
A command that can be run to make the specified keyboard and keymap A command that can be run to make the specified keyboard and keymap
""" """
if target is None: make_args = [keyboard, keymap]
return ['make', ':'.join((keyboard, keymap))]
return ['make', ':'.join((keyboard, keymap, target))]
if target:
make_args.append(target)
def parse_configurator_json(configurator_file): return ['make', ':'.join(make_args)]
"""Open and parse a configurator json export
"""
user_keymap = json.load(configurator_file)
return user_keymap
def compile_configurator_json(user_keymap, bootloader=None): def compile_configurator_json(user_keymap, bootloader=None):
"""Convert a configurator export JSON file into a C file """Convert a configurator export JSON file into a C file
Args: Args:
configurator_filename configurator_filename
The configurator JSON export file The configurator JSON export file
@ -43,6 +47,7 @@ def compile_configurator_json(user_keymap, bootloader=None):
A bootloader to flash A bootloader to flash
Returns: Returns:
A command to run to compile and flash the C file. A command to run to compile and flash the C file.
""" """
# Write the keymap C file # Write the keymap C file
@ -52,3 +57,76 @@ def compile_configurator_json(user_keymap, bootloader=None):
if bootloader is None: if bootloader is None:
return create_make_command(user_keymap['keyboard'], user_keymap['keymap']) return create_make_command(user_keymap['keyboard'], user_keymap['keymap'])
return create_make_command(user_keymap['keyboard'], user_keymap['keymap'], bootloader) return create_make_command(user_keymap['keyboard'], user_keymap['keymap'], bootloader)
def find_keyboard_keymap():
"""Returns `(keyboard_name, keymap_name)` based on the user's current environment.
This determines the keyboard and keymap name using the following precedence order:
* Command line flags (--keyboard and --keymap)
* Current working directory
* `keyboards/<keyboard_name>`
* `keyboards/<keyboard_name>/keymaps/<keymap_name>`
* `layouts/**/<keymap_name>`
* `users/<keymap_name>`
* Configuration
* cli.config.<subcommand>.keyboard
* cli.config.<subcommand>.keymap
"""
# Check to make sure their copy of MILC supports config_source
if not hasattr(cli, 'config_source'):
cli.log.error("Your QMK CLI is out of date. Please upgrade using pip3 or your package manager.")
exit(1)
# State variables
relative_cwd = under_qmk_firmware()
keyboard_name = ""
keymap_name = ""
# If the keyboard or keymap are passed as arguments use that in preference to anything else
if cli.config_source[cli._entrypoint.__name__]['keyboard'] == 'argument':
keyboard_name = cli.config[cli._entrypoint.__name__]['keyboard']
if cli.config_source[cli._entrypoint.__name__]['keymap'] == 'argument':
keymap_name = cli.config[cli._entrypoint.__name__]['keymap']
if not keyboard_name or not keymap_name:
# If we don't have a keyboard_name and keymap_name from arguments try to derive one or both
if relative_cwd and relative_cwd.parts and relative_cwd.parts[0] == 'keyboards':
# Try to determine the keyboard and/or keymap name
current_path = Path('/'.join(relative_cwd.parts[1:]))
if current_path.parts[-2] == 'keymaps':
if not keymap_name:
keymap_name = current_path.parts[-1]
if not keyboard_name:
keyboard_name = '/'.join(current_path.parts[:-2])
elif not keyboard_name and is_keyboard(current_path):
keyboard_name = str(current_path)
elif relative_cwd and relative_cwd.parts and relative_cwd.parts[0] == 'layouts':
# Try to determine the keymap name from the community layout
if is_keymap_dir(relative_cwd) and not keymap_name:
keymap_name = relative_cwd.name
elif relative_cwd and relative_cwd.parts and relative_cwd.parts[0] == 'users':
# Try to determine the keymap name based on which userspace they're in
if not keymap_name and len(relative_cwd.parts) > 1:
keymap_name = relative_cwd.parts[1]
# If we still don't have a keyboard and keymap check the config
if not keyboard_name and cli.config[cli._entrypoint.__name__]['keyboard']:
keyboard_name = cli.config[cli._entrypoint.__name__]['keyboard']
if not keymap_name and cli.config[cli._entrypoint.__name__]['keymap']:
keymap_name = cli.config[cli._entrypoint.__name__]['keymap']
return (keyboard_name, keymap_name)
def parse_configurator_json(configurator_file):
"""Open and parse a configurator json export
"""
user_keymap = json.load(configurator_file)
return user_keymap

View File

@ -0,0 +1,9 @@
"""Information that should be available to the python library.
"""
from pathlib import Path
# The root of the qmk_firmware tree.
QMK_FIRMWARE = Path.cwd()
# This is the number of directories under `qmk_firmware/keyboards` that will be traversed. This is currently a limitation of our make system.
MAX_KEYBOARD_SUBFOLDERS = 5

View File

@ -31,11 +31,10 @@ def template(keyboard):
keyboard keyboard
The keyboard to return a template for. The keyboard to return a template for.
""" """
template_name = 'keyboards/%s/templates/keymap.c' % keyboard template_file = Path('keyboards/%s/templates/keymap.c' % keyboard)
if os.path.exists(template_name): if template_file.exists():
with open(template_name, 'r') as fd: return template_file.read_text()
return fd.read()
return DEFAULT_KEYMAP_C return DEFAULT_KEYMAP_C
@ -85,15 +84,10 @@ def write(keyboard, keymap, layout, layers):
An array of arrays describing the keymap. Each item in the inner array should be a string that is a valid QMK keycode. An array of arrays describing the keymap. Each item in the inner array should be a string that is a valid QMK keycode.
""" """
keymap_c = generate(keyboard, layout, layers) keymap_c = generate(keyboard, layout, layers)
keymap_path = qmk.path.keymap(keyboard) keymap_file = qmk.path.keymap(keyboard) / keymap / 'keymap.c'
keymap_dir = os.path.join(keymap_path, keymap)
keymap_file = os.path.join(keymap_dir, 'keymap.c')
if not os.path.exists(keymap_dir): keymap_file.parent.mkdir(parents=True, exist_ok=True)
os.makedirs(keymap_dir) keymap_file.write_text(keymap_c)
with open(keymap_file, 'w') as keymap_fd:
keymap_fd.write(keymap_c)
return keymap_file return keymap_file

View File

@ -2,34 +2,69 @@
""" """
import logging import logging
import os import os
from pathlib import Path
from qmk.constants import QMK_FIRMWARE, MAX_KEYBOARD_SUBFOLDERS
from qmk.errors import NoSuchKeyboardError from qmk.errors import NoSuchKeyboardError
def is_keymap_dir(keymap_path):
"""Returns True if `keymap_path` is a valid keymap directory.
"""
keymap_path = Path(keymap_path)
keymap_c = keymap_path / 'keymap.c'
keymap_json = keymap_path / 'keymap.json'
return any((keymap_c.exists(), keymap_json.exists()))
def is_keyboard(keyboard_name):
"""Returns True if `keyboard_name` is a keyboard we can compile.
"""
keyboard_path = QMK_FIRMWARE / 'keyboards' / keyboard_name
rules_mk = keyboard_path / 'rules.mk'
return rules_mk.exists()
def under_qmk_firmware():
"""Returns a Path object representing the relative path under qmk_firmware, or None.
"""
cwd = Path(os.environ['ORIG_CWD'])
try:
return cwd.relative_to(QMK_FIRMWARE)
except ValueError:
return None
def keymap(keyboard): def keymap(keyboard):
"""Locate the correct directory for storing a keymap. """Locate the correct directory for storing a keymap.
Args: Args:
keyboard keyboard
The name of the keyboard. Example: clueboard/66/rev3 The name of the keyboard. Example: clueboard/66/rev3
""" """
for directory in ['.', '..', '../..', '../../..', '../../../..', '../../../../..']: keyboard_folder = Path('keyboards') / keyboard
basepath = os.path.normpath(os.path.join('keyboards', keyboard, directory, 'keymaps'))
if os.path.exists(basepath): for i in range(MAX_KEYBOARD_SUBFOLDERS):
return basepath if (keyboard_folder / 'keymaps').exists():
return (keyboard_folder / 'keymaps').resolve()
logging.error('Could not find keymaps directory!') keyboard_folder = keyboard_folder.parent
logging.error('Could not find the keymaps directory!')
raise NoSuchKeyboardError('Could not find keymaps directory for: %s' % keyboard) raise NoSuchKeyboardError('Could not find keymaps directory for: %s' % keyboard)
def normpath(path): def normpath(path):
"""Returns the fully resolved absolute path to a file. """Returns a `pathlib.Path()` object for a given path.
This function will return the absolute path to a file as seen from the This will use the path to a file as seen from the directory the script was called from. You should use this to normalize filenames supplied from the command line.
directory the script was called from.
""" """
if path and path[0] == '/': path = Path(path)
return os.path.normpath(path)
return os.path.normpath(os.path.join(os.environ['ORIG_CWD'], path)) if path.is_absolute():
return Path(path)
return Path(os.environ['ORIG_CWD']) / path

View File

@ -1,13 +1,14 @@
import os import os
from pathlib import Path
import qmk.path import qmk.path
def test_keymap_onekey_pytest(): def test_keymap_onekey_pytest():
path = qmk.path.keymap('handwired/onekey/pytest') path = qmk.path.keymap('handwired/onekey/pytest')
assert path == 'keyboards/handwired/onekey/keymaps' assert path.samefile('keyboards/handwired/onekey/keymaps')
def test_normpath(): def test_normpath():
path = qmk.path.normpath('lib/python') path = qmk.path.normpath('lib/python')
assert path == os.path.join(os.environ['ORIG_CWD'], 'lib/python') assert path.samefile(Path(os.environ['ORIG_CWD']) / 'lib/python')