mirror of
https://github.com/qmk/qmk_cli.git
synced 2026-07-25 16:42:55 -04:00
135 lines
3.6 KiB
Python
135 lines
3.6 KiB
Python
"""Useful helper functions.
|
|
"""
|
|
import os
|
|
import sys
|
|
import stat
|
|
import json
|
|
import shutil
|
|
from functools import lru_cache
|
|
from pathlib import Path
|
|
|
|
from milc import cli
|
|
|
|
|
|
def AbsPath(arg): # noqa: N802
|
|
"""Resolve relative paths to the original working directory.
|
|
"""
|
|
arg = Path(arg)
|
|
if not arg.is_absolute():
|
|
return (Path(os.environ['ORIG_CWD']) / arg).absolute()
|
|
|
|
return arg
|
|
|
|
|
|
def rmtree(path):
|
|
"""Safe version of shutil.rmtree which handles errors on Windows where some of the files have their read-only bit set."""
|
|
def remove_readonly(func, path, _):
|
|
"""Clear the readonly bit and reattempt the removal."""
|
|
os.chmod(path, stat.S_IWRITE)
|
|
func(path)
|
|
|
|
if sys.version_info >= (3, 12):
|
|
# See https://docs.python.org/3.12/whatsnew/3.12.html#shutil
|
|
return shutil.rmtree(path, onexc=remove_readonly)
|
|
else:
|
|
return shutil.rmtree(path, onerror=remove_readonly)
|
|
|
|
|
|
def is_qmk_firmware(qmk_firmware):
|
|
"""Returns True if the given Path() is a qmk_firmware clone.
|
|
"""
|
|
paths = [
|
|
qmk_firmware,
|
|
qmk_firmware / 'quantum',
|
|
qmk_firmware / 'requirements.txt',
|
|
qmk_firmware / 'requirements-dev.txt',
|
|
qmk_firmware / 'lib/python/qmk/cli/__init__.py',
|
|
]
|
|
|
|
for path in paths:
|
|
if not path.exists():
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
@lru_cache(maxsize=1)
|
|
def find_qmk_firmware():
|
|
"""Look for qmk_firmware in the usual places.
|
|
|
|
This function returns the path to qmk_firmware, or the default location if one does not exist.
|
|
"""
|
|
if in_qmk_firmware():
|
|
return in_qmk_firmware()
|
|
|
|
if cli.config.user.qmk_home:
|
|
return Path(cli.config.user.qmk_home).expanduser().resolve()
|
|
|
|
if 'QMK_HOME' in os.environ:
|
|
path = Path(os.environ['QMK_HOME']).expanduser()
|
|
if path.exists():
|
|
return path.resolve()
|
|
return path
|
|
|
|
return Path.home() / 'qmk_firmware'
|
|
|
|
|
|
@lru_cache(maxsize=1)
|
|
def in_qmk_firmware():
|
|
"""Returns the path to the qmk_firmware we are currently in, or None if we are not inside qmk_firmware.
|
|
"""
|
|
cur_dir = Path.cwd()
|
|
while len(cur_dir.parents) > 0:
|
|
if is_qmk_firmware(cur_dir):
|
|
return cur_dir
|
|
|
|
# Move up a directory before the next iteration
|
|
cur_dir = cur_dir / '..'
|
|
cur_dir = cur_dir.resolve()
|
|
|
|
|
|
def is_qmk_userspace(qmk_userspace):
|
|
"""Returns True if the given Path() is a qmk_userspace clone.
|
|
"""
|
|
path = qmk_userspace / 'qmk.json'
|
|
if not path.exists():
|
|
return False
|
|
|
|
try:
|
|
return 'userspace_version' in json.loads(path.read_text(encoding="UTF-8"))
|
|
except json.decoder.JSONDecodeError:
|
|
return False
|
|
|
|
|
|
@lru_cache(maxsize=1)
|
|
def find_qmk_userspace():
|
|
"""Look for qmk_userspace in the usual places.
|
|
"""
|
|
if in_qmk_userspace():
|
|
return in_qmk_userspace()
|
|
|
|
if 'QMK_USERSPACE' in os.environ:
|
|
path = Path(os.environ['QMK_USERSPACE']).expanduser()
|
|
if path.exists():
|
|
return path.resolve()
|
|
return path
|
|
|
|
if cli.config.user.overlay_dir:
|
|
return Path(cli.config.user.overlay_dir).expanduser().resolve()
|
|
|
|
return Path.home() / 'qmk_userspace'
|
|
|
|
|
|
@lru_cache(maxsize=1)
|
|
def in_qmk_userspace():
|
|
"""Returns the path to the qmk_userspace we are currently in, or None if we are not inside qmk_userspace.
|
|
"""
|
|
cur_dir = Path.cwd()
|
|
while len(cur_dir.parents) > 0:
|
|
if is_qmk_userspace(cur_dir):
|
|
return cur_dir
|
|
|
|
# Move up a directory before the next iteration
|
|
cur_dir = cur_dir / '..'
|
|
cur_dir = cur_dir.resolve()
|