path.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. """Functions that help us work with files and folders.
  2. """
  3. import logging
  4. import os
  5. import argparse
  6. from pathlib import Path
  7. from qmk.constants import MAX_KEYBOARD_SUBFOLDERS, QMK_FIRMWARE, QMK_USERSPACE, HAS_QMK_USERSPACE
  8. from qmk.errors import NoSuchKeyboardError
  9. def is_keyboard(keyboard_name):
  10. """Returns True if `keyboard_name` is a keyboard we can compile.
  11. """
  12. if keyboard_name:
  13. keyboard_path = QMK_FIRMWARE / 'keyboards' / keyboard_name
  14. rules_mk = keyboard_path / 'rules.mk'
  15. keyboard_json = keyboard_path / 'keyboard.json'
  16. return rules_mk.exists() or keyboard_json.exists()
  17. def under_qmk_firmware(path=Path(os.environ['ORIG_CWD'])):
  18. """Returns a Path object representing the relative path under qmk_firmware, or None.
  19. """
  20. try:
  21. return path.relative_to(QMK_FIRMWARE)
  22. except ValueError:
  23. return None
  24. def under_qmk_userspace(path=Path(os.environ['ORIG_CWD'])):
  25. """Returns a Path object representing the relative path under $QMK_USERSPACE, or None.
  26. """
  27. try:
  28. if HAS_QMK_USERSPACE:
  29. return path.relative_to(QMK_USERSPACE)
  30. except ValueError:
  31. pass
  32. return None
  33. def is_under_qmk_firmware(path=Path(os.environ['ORIG_CWD'])):
  34. """Returns a boolean if the input path is a child under qmk_firmware.
  35. """
  36. if path is None:
  37. return False
  38. try:
  39. return Path(os.path.commonpath([Path(path), QMK_FIRMWARE])) == QMK_FIRMWARE
  40. except ValueError:
  41. return False
  42. def is_under_qmk_userspace(path=Path(os.environ['ORIG_CWD'])):
  43. """Returns a boolean if the input path is a child under $QMK_USERSPACE.
  44. """
  45. if path is None:
  46. return False
  47. try:
  48. if HAS_QMK_USERSPACE:
  49. return Path(os.path.commonpath([Path(path), QMK_USERSPACE])) == QMK_USERSPACE
  50. except ValueError:
  51. return False
  52. def keyboard(keyboard_name):
  53. """Returns the path to a keyboard's directory relative to the qmk root.
  54. """
  55. return Path('keyboards') / keyboard_name
  56. def keymaps(keyboard_name):
  57. """Returns all of the `keymaps/` directories for a given keyboard.
  58. Args:
  59. keyboard_name
  60. The name of the keyboard. Example: clueboard/66/rev3
  61. """
  62. keyboard_folder = keyboard(keyboard_name)
  63. found_dirs = []
  64. if HAS_QMK_USERSPACE:
  65. this_keyboard_folder = Path(QMK_USERSPACE) / keyboard_folder
  66. for _ in range(MAX_KEYBOARD_SUBFOLDERS):
  67. if (this_keyboard_folder / 'keymaps').exists():
  68. found_dirs.append((this_keyboard_folder / 'keymaps').resolve())
  69. this_keyboard_folder = this_keyboard_folder.parent
  70. if this_keyboard_folder.resolve() == QMK_USERSPACE.resolve():
  71. break
  72. # We don't have any relevant keymap directories in userspace, so we'll use the fully-qualified path instead.
  73. if len(found_dirs) == 0:
  74. found_dirs.append((QMK_USERSPACE / keyboard_folder / 'keymaps').resolve())
  75. this_keyboard_folder = QMK_FIRMWARE / keyboard_folder
  76. for _ in range(MAX_KEYBOARD_SUBFOLDERS):
  77. if (this_keyboard_folder / 'keymaps').exists():
  78. found_dirs.append((this_keyboard_folder / 'keymaps').resolve())
  79. this_keyboard_folder = this_keyboard_folder.parent
  80. if this_keyboard_folder.resolve() == QMK_FIRMWARE.resolve():
  81. break
  82. if len(found_dirs) > 0:
  83. return found_dirs
  84. logging.error('Could not find the keymaps directory!')
  85. raise NoSuchKeyboardError('Could not find keymaps directory for: %s' % keyboard_name)
  86. def keymap(keyboard_name, keymap_name):
  87. """Locate the directory of a given keymap.
  88. Args:
  89. keyboard_name
  90. The name of the keyboard. Example: clueboard/66/rev3
  91. keymap_name
  92. The name of the keymap. Example: default
  93. """
  94. for keymap_dir in keymaps(keyboard_name):
  95. if (keymap_dir / keymap_name).exists():
  96. return (keymap_dir / keymap_name).resolve()
  97. def normpath(path):
  98. """Returns a `pathlib.Path()` object for a given path.
  99. 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.
  100. """
  101. path = Path(path)
  102. if path.is_absolute():
  103. return path
  104. return Path(os.environ['ORIG_CWD']) / path
  105. class FileType(argparse.FileType):
  106. def __init__(self, *args, **kwargs):
  107. # Use UTF8 by default for stdin
  108. if 'encoding' not in kwargs:
  109. kwargs['encoding'] = 'UTF-8'
  110. return super().__init__(*args, **kwargs)
  111. def __call__(self, string):
  112. """normalize and check exists
  113. otherwise magic strings like '-' for stdin resolve to bad paths
  114. """
  115. norm = normpath(string)
  116. return norm if norm.exists() else super().__call__(string)