summaryrefslogtreecommitdiff
path: root/lib/python/qmk/path.py
blob: 74364ee04b02440450887269e21dae1db89cd611 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
"""Functions that help us work with files and folders.
"""
import logging
import os
import argparse
from pathlib import Path

from qmk.constants import MAX_KEYBOARD_SUBFOLDERS, QMK_FIRMWARE, QMK_USERSPACE, HAS_QMK_USERSPACE
from qmk.errors import NoSuchKeyboardError


def is_keyboard(keyboard_name):
    """Returns True if `keyboard_name` is a keyboard we can compile.
    """
    if keyboard_name:
        keyboard_path = QMK_FIRMWARE / 'keyboards' / keyboard_name
        rules_mk = keyboard_path / 'rules.mk'

        return rules_mk.exists()


def under_qmk_firmware(path=Path(os.environ['ORIG_CWD'])):
    """Returns a Path object representing the relative path under qmk_firmware, or None.
    """
    try:
        return path.relative_to(QMK_FIRMWARE)
    except ValueError:
        return None


def under_qmk_userspace(path=Path(os.environ['ORIG_CWD'])):
    """Returns a Path object representing the relative path under $QMK_USERSPACE, or None.
    """
    try:
        if HAS_QMK_USERSPACE:
            return path.relative_to(QMK_USERSPACE)
    except ValueError:
        pass
    return None


def is_under_qmk_firmware(path=Path(os.environ['ORIG_CWD'])):
    """Returns a boolean if the input path is a child under qmk_firmware.
    """
    if path is None:
        return False
    try:
        return Path(os.path.commonpath([Path(path), QMK_FIRMWARE])) == QMK_FIRMWARE
    except ValueError:
        return False


def is_under_qmk_userspace(path=Path(os.environ['ORIG_CWD'])):
    """Returns a boolean if the input path is a child under $QMK_USERSPACE.
    """
    if path is None:
        return False
    try:
        if HAS_QMK_USERSPACE:
            return Path(os.path.commonpath([Path(path), QMK_USERSPACE])) == QMK_USERSPACE
    except ValueError:
        return False


def keyboard(keyboard_name):
    """Returns the path to a keyboard's directory relative to the qmk root.
    """
    return Path('keyboards') / keyboard_name


def keymaps(keyboard_name):
    """Returns all of the `keymaps/` directories for a given keyboard.

    Args:

        keyboard_name
            The name of the keyboard. Example: clueboard/66/rev3
    """
    keyboard_folder = keyboard(keyboard_name)
    found_dirs = []

    if HAS_QMK_USERSPACE:
        this_keyboard_folder = Path(QMK_USERSPACE) / keyboard_folder
        for _ in range(MAX_KEYBOARD_SUBFOLDERS):
            if (this_keyboard_folder / 'keymaps').exists():
                found_dirs.append((this_keyboard_folder / 'keymaps').resolve())

            this_keyboard_folder = this_keyboard_folder.parent
            if this_keyboard_folder.resolve() == QMK_USERSPACE.resolve():
                break

        # We don't have any relevant keymap directories in userspace, so we'll use the fully-qualified path instead.
        if len(found_dirs) == 0:
            found_dirs.append((QMK_USERSPACE / keyboard_folder / 'keymaps').resolve())

    this_keyboard_folder = QMK_FIRMWARE / keyboard_folder
    for _ in range(MAX_KEYBOARD_SUBFOLDERS):
        if (this_keyboard_folder / 'keymaps').exists():
            found_dirs.append((this_keyboard_folder / 'keymaps').resolve())

        this_keyboard_folder = this_keyboard_folder.parent
        if this_keyboard_folder.resolve() == QMK_FIRMWARE.resolve():
            break

    if len(found_dirs) > 0:
        return found_dirs

    logging.error('Could not find the keymaps directory!')
    raise NoSuchKeyboardError('Could not find keymaps directory for: %s' % keyboard_name)


def keymap(keyboard_name, keymap_name):
    """Locate the directory of a given keymap.

    Args:

        keyboard_name
            The name of the keyboard. Example: clueboard/66/rev3
        keymap_name
            The name of the keymap. Example: default
    """
    for keymap_dir in keymaps(keyboard_name):
        if (keymap_dir / keymap_name).exists():
            return (keymap_dir / keymap_name).resolve()


def normpath(path):
    """Returns a `pathlib.Path()` object for a given path.

    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.
    """
    path = Path(path)

    if path.is_absolute():
        return path

    return Path(os.environ['ORIG_CWD']) / path


class FileType(argparse.FileType):
    def __init__(self, *args, **kwargs):
        # Use UTF8 by default for stdin
        if 'encoding' not in kwargs:
            kwargs['encoding'] = 'UTF-8'
        return super().__init__(*args, **kwargs)

    def __call__(self, string):
        """normalize and check exists
            otherwise magic strings like '-' for stdin resolve to bad paths
        """
        norm = normpath(string)
        return norm if norm.exists() else super().__call__(string)