summaryrefslogtreecommitdiffstats
path: root/lib/python/qmk/decorators.py
blob: 629402b0957c9c49e9bc7e49c2b00bff7ecb45fd (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
"""Helpful decorators that subcommands can use.
"""
import functools
from pathlib import Path
from time import monotonic

from milc import cli

from qmk.keymap import is_keymap_dir
from qmk.path import is_keyboard, under_qmk_firmware


def automagic_keyboard(func):
    """Sets `cli.config.<subcommand>.keyboard` based on environment.

    This will rewrite cli.config.<subcommand>.keyboard if the user did not pass `--keyboard` and the directory they are currently in is a keyboard or keymap directory.
    """
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        # Check to make sure their copy of MILC supports config_source
        if not hasattr(cli, 'config_source'):
            cli.log.error("This subcommand requires a newer version of the QMK CLI. Please upgrade using `pip3 install --upgrade qmk` or your package manager.")
            exit(1)

        # Ensure that `--keyboard` was not passed and CWD is under `qmk_firmware/keyboards`
        if cli.config_source[cli._entrypoint.__name__]['keyboard'] != 'argument':
            relative_cwd = under_qmk_firmware()

            if relative_cwd and len(relative_cwd.parts) > 1 and relative_cwd.parts[0] == 'keyboards':
                # Attempt to extract the keyboard name from the current directory
                current_path = Path('/'.join(relative_cwd.parts[1:]))

                if 'keymaps' in current_path.parts:
                    # Strip current_path of anything after `keymaps`
                    keymap_index = len(current_path.parts) - current_path.parts.index('keymaps') - 1
                    current_path = current_path.parents[keymap_index]

                if is_keyboard(current_path):
                    cli.config[cli._entrypoint.__name__]['keyboard'] = str(current_path)
                    cli.config_source[cli._entrypoint.__name__]['keyboard'] = 'keyboard_directory'

        return func(*args, **kwargs)

    return wrapper


def automagic_keymap(func):
    """Sets `cli.config.<subcommand>.keymap` based on environment.

    This will rewrite cli.config.<subcommand>.keymap if the user did not pass `--keymap` and the directory they are currently in is a keymap, layout, or user directory.
    """
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        # Check to make sure their copy of MILC supports config_source
        if not hasattr(cli, 'config_source'):
            cli.log.error("This subcommand requires a newer version of the QMK CLI. Please upgrade using `pip3 install --upgrade qmk` or your package manager.")
            exit(1)

        # Ensure that `--keymap` was not passed and that we're under `qmk_firmware`
        if cli.config_source[cli._entrypoint.__name__]['keymap'] != 'argument':
            relative_cwd = under_qmk_firmware()

            if relative_cwd and len(relative_cwd.parts) > 1:
                # If we're in `qmk_firmware/keyboards` and `keymaps` is in our path, try to find the keyboard name.
                if relative_cwd.parts[0] == 'keyboards' and 'keymaps' in relative_cwd.parts:
                    current_path = Path('/'.join(relative_cwd.parts[1:]))  # Strip 'keyboards' from the front

                    if 'keymaps' in current_path.parts and current_path.name != 'keymaps':
                        while current_path.parent.name != 'keymaps':
                            current_path = current_path.parent
                        cli.config[cli._entrypoint.__name__]['keymap'] = current_path.name
                        cli.config_source[cli._entrypoint.__name__]['keymap'] = 'keymap_directory'

                # If we're in `qmk_firmware/layouts` guess the name from the community keymap they're in
                elif relative_cwd.parts[0] == 'layouts' and is_keymap_dir(relative_cwd):
                    cli.config[cli._entrypoint.__name__]['keymap'] = relative_cwd.name
                    cli.config_source[cli._entrypoint.__name__]['keymap'] = 'layouts_directory'

                # If we're in `qmk_firmware/users` guess the name from the userspace they're in
                elif relative_cwd.parts[0] == 'users':
                    # Guess the keymap name based on which userspace they're in
                    cli.config[cli._entrypoint.__name__]['keymap'] = relative_cwd.parts[1]
                    cli.config_source[cli._entrypoint.__name__]['keymap'] = 'users_directory'

        return func(*args, **kwargs)

    return wrapper


def lru_cache(timeout=10, maxsize=128, typed=False):
    """Least Recently Used Cache- cache the result of a function.

    Args:

        timeout
            How many seconds to cache results for.

        maxsize
            The maximum size of the cache in bytes

        typed
            When `True` argument types will be taken into consideration, for example `3` and `3.0` will be treated as different keys.
    """
    def wrapper_cache(func):
        func = functools.lru_cache(maxsize=maxsize, typed=typed)(func)
        func.expiration = monotonic() + timeout

        @functools.wraps(func)
        def wrapped_func(*args, **kwargs):
            if monotonic() >= func.expiration:
                func.expiration = monotonic() + timeout

                func.cache_clear()

            return func(*args, **kwargs)

        wrapped_func.cache_info = func.cache_info
        wrapped_func.cache_clear = func.cache_clear

        return wrapped_func

    return wrapper_cache