Works towars typing keys.py

This commit is contained in:
sofubi 2021-08-18 20:39:18 -04:00
parent 908da846fe
commit 36ea0eec4e
2 changed files with 124 additions and 50 deletions

View File

@ -1,3 +1,6 @@
from kmk.keys import KeyAttrDict
from typing import Any, Optional
from kmk.kmk_keyboard import KMKKeyboard
from kmk.kmktime import sleep_ms
@ -5,7 +8,15 @@ def passthrough(key, keyboard, *args, **kwargs):
return keyboard
def default_pressed(key, keyboard, KC, coord_int=None, coord_raw=None, *args, **kwargs):
def default_pressed(
key: str,
keyboard: KMKKeyboard,
KC: KeyAttrDict,
coord_int: Optional[int] = None,
coord_raw: Optional[str] = None,
*args: Any,
**kwargs: Any,
) -> KMKKeyboard:
keyboard.hid_pending = True
if coord_int is not None:
@ -17,7 +28,13 @@ def default_pressed(key, keyboard, KC, coord_int=None, coord_raw=None, *args, **
def default_released(
key, keyboard, KC, coord_int=None, coord_raw=None, *args, **kwargs # NOQA
key: str,
keyboard: KMKKeyboard,
KC: KeyAttrDict,
coord_int: Optional[int] = None,
coord_raw: Optional[str] = None,
*args: Any,
**kwargs: Any, # NOQA
):
keyboard.hid_pending = True
keyboard.keys_pressed.discard(key)

View File

@ -1,6 +1,11 @@
import gc
from kmk.kmk_keyboard import KMKKeyboard
from micropython import const
from __future__ import annotations
from typing import Any, Callable, List, Optional, Protocol, Set, Tuple, Union
import kmk.handlers.stock as handlers
from kmk.consts import UnicodeMode
from kmk.key_validators import (
@ -10,19 +15,29 @@ from kmk.key_validators import (
)
from kmk.types import AttrDict, UnicodeModeKeyMeta
DEBUG_OUTPUT = False
DEBUG_OUTPUT: bool = False
FIRST_KMK_INTERNAL_KEY = const(1000)
NEXT_AVAILABLE_KEY = 1000
FIRST_KMK_INTERNAL_KEY: int = const(1000)
NEXT_AVAILABLE_KEY: int = 1000
KEY_SIMPLE = const(0)
KEY_MODIFIER = const(1)
KEY_CONSUMER = const(2)
KEY_SIMPLE: int = const(0)
KEY_MODIFIER: int = const(1)
KEY_CONSUMER: int = const(2)
ALL_ALPHAS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
ALL_NUMBERS = '1234567890'
ALL_ALPHAS: str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
ALL_NUMBERS: str = '1234567890'
# since KC.1 isn't valid Python, alias to KC.N1
ALL_NUMBER_ALIASES = tuple(f'N{x}' for x in ALL_NUMBERS)
ALL_NUMBER_ALIASES: Tuple[str, ...] = tuple(f'N{x}' for x in ALL_NUMBERS)
KeyReturn = Union[Key, ModifierKey, ConsumerKey, None]
class LeftPipeCallback(Protocol):
def __call__(
self, candidate: str, code: Union[str, int], names: Tuple[str, ...]
) -> KeyReturn:
...
class InfiniteLoopDetected(Exception):
@ -31,42 +46,53 @@ class InfiniteLoopDetected(Exception):
# this is a bit of an FP style thing - combining a pipe operator a-la F# with
# a bootleg Maybe monad to clean up these make_key sequences
def left_pipe_until_some(candidate, functor, *args_iter):
def left_pipe_until_some(
candidate: str,
functor: LeftPipeCallback,
*args_iter: Tuple[Union[str, int], Tuple[str, ...]],
) -> KeyReturn:
for args in args_iter:
result = functor(candidate, *args)
if result is not None:
return result
def first_truthy(candidate, *funcs):
def first_truthy(
candidate: str,
*funcs: Callable[[str], Union[Key, ModifierKey, ConsumerKey, None]],
) -> KeyReturn:
for func in funcs:
result = func(candidate)
if result is not None:
return result
def maybe_make_mod_key(candidate, code, names):
def maybe_make_mod_key(candidate: str, code: int, names: Tuple[str, ...]) -> KeyReturn:
if candidate in names:
return make_mod_key(code=code, names=names)
def maybe_make_key(candidate, code, names):
def maybe_make_key(candidate: str, code: int, names: Tuple[str, ...]) -> KeyReturn:
if candidate in names:
return make_key(code=code, names=names)
def maybe_make_shifted_key(candidate, target_name, names):
def maybe_make_shifted_key(
candidate: str, target_name: str, names: Tuple[str, ...]
) -> KeyReturn:
if candidate in names:
return make_shifted_key(target_name=target_name, names=names)
def maybe_make_consumer_key(candidate, code, names):
def maybe_make_consumer_key(
candidate: str, code: int, names: Tuple[str, ...]
) -> KeyReturn:
if candidate in names:
return make_consumer_key(code=code, names=names)
class KeyAttrDict(AttrDict):
def __getattr__(self, key, depth=0):
def __getattr__(self, key: str, depth: int = 0) -> str:
if depth > 1:
raise InfiniteLoopDetected()
@ -384,32 +410,54 @@ class KeyAttrDict(AttrDict):
KC = KeyAttrDict()
class DefaultPressRelease(Protocol):
def __call__(
self,
key: Key,
keyboard: KMKKeyboard,
KC: KeyAttrDict,
coord_int: Optional[int],
coord_raw: Optional[str],
*args: Any,
**kwargs: Any,
) -> KMKKeyboard:
...
Handler = Callable[[Any, KMKKeyboard, KeyAttrDict, int, str], KMKKeyboard]
HandlerList = List[Handler]
class Key:
def __init__(
self,
code,
has_modifiers=None,
no_press=False,
no_release=False,
on_press=handlers.default_pressed,
on_release=handlers.default_released,
meta=object(),
):
self.code = code
self.has_modifiers = has_modifiers
code: int,
has_modifiers: Optional[Set[int]] = None,
no_press: Optional[bool] = False,
no_release: Optional[bool] = False,
on_press: DefaultPressRelease = handlers.default_pressed,
on_release: DefaultPressRelease = handlers.default_released,
meta: object = object(),
) -> None:
self.code: int = code
self.has_modifiers: Optional[Set[int]] = has_modifiers
# cast to bool() in case we get a None value
self.no_press = bool(no_press)
self.no_release = bool(no_press)
self.no_press: bool = bool(no_press)
self.no_release: bool = bool(no_press)
self._pre_press_handlers = []
self._post_press_handlers = []
self._pre_release_handlers = []
self._post_release_handlers = []
self._handle_press = on_press
self._handle_release = on_release
self.meta = meta
self._pre_press_handlers: HandlerList = []
self._post_press_handlers: HandlerList = []
self._pre_release_handlers: HandlerList = []
self._post_release_handlers: HandlerList = []
self._handle_press: DefaultPressRelease = on_press
self._handle_release: DefaultPressRelease = on_release
self.meta: object = meta
def __call__(self, no_press=None, no_release=None):
def __call__(
self, no_press: Optional[bool] = None, no_release: Optional[bool] = None
) -> Key:
if no_press is None and no_release is None:
return self
@ -423,7 +471,9 @@ class Key:
def __repr__(self):
return 'Key(code={}, has_modifiers={})'.format(self.code, self.has_modifiers)
def on_press(self, state, coord_int, coord_raw):
def on_press(
self, state: KMKKeyboard, coord_int: int, coord_raw: str
) -> Union[KMKKeyboard, None]:
for fn in self._pre_press_handlers:
if not fn(self, state, KC, coord_int, coord_raw):
return None
@ -435,7 +485,9 @@ class Key:
return ret
def on_release(self, state, coord_int, coord_raw):
def on_release(
self, state: KMKKeyboard, coord_int: int, coord_raw: str
) -> Union[KMKKeyboard, None]:
for fn in self._pre_release_handlers:
if not fn(self, state, KC, coord_int, coord_raw):
return None
@ -447,7 +499,7 @@ class Key:
return ret
def clone(self):
def clone(self) -> Key:
'''
Return a shallow clone of the current key without any pre/post press/release
handlers attached. Almost exclusively useful for creating non-colliding keys
@ -464,7 +516,7 @@ class Key:
meta=self.meta,
)
def before_press_handler(self, fn):
def before_press_handler(self, fn: Handler) -> Key:
'''
Attach a callback to be run prior to the on_press handler for this key.
Receives the following:
@ -488,7 +540,7 @@ class Key:
self._pre_press_handlers.append(fn)
return self
def after_press_handler(self, fn):
def after_press_handler(self, fn: Handler) -> Key:
'''
Attach a callback to be run after the on_release handler for this key.
Receives the following:
@ -511,7 +563,7 @@ class Key:
self._post_press_handlers.append(fn)
return self
def before_release_handler(self, fn):
def before_release_handler(self, fn: Handler) -> Key:
'''
Attach a callback to be run prior to the on_release handler for this
key. Receives the following:
@ -535,7 +587,7 @@ class Key:
self._pre_release_handlers.append(fn)
return self
def after_release_handler(self, fn):
def after_release_handler(self, fn: Handler) -> Key:
'''
Attach a callback to be run after the on_release handler for this key.
Receives the following:
@ -563,9 +615,14 @@ class ModifierKey(Key):
# FIXME this is atrocious to read. Please, please, please, strike down upon
# this with great vengeance and furious anger.
FAKE_CODE = const(-1)
FAKE_CODE: int = const(-1)
def __call__(self, modified_code=None, no_press=None, no_release=None):
def __call__(
self,
modified_code: Optional[Key] = None,
no_press: Optional[bool] = None,
no_release: Optional[bool] = None,
) -> Union[Key, ModifierKey]:
if modified_code is None and no_press is None and no_release is None:
return self
@ -598,7 +655,7 @@ class ModifierKey(Key):
return new_keycode
def __repr__(self):
def __repr__(self) -> str:
return 'ModifierKey(code={}, has_modifiers={})'.format(
self.code, self.has_modifiers
)
@ -608,7 +665,7 @@ class ConsumerKey(Key):
pass
def register_key_names(key, names=tuple()): # NOQA
def register_key_names(key: Key, names: Tuple[str, ...] = tuple()): # NOQA
'''
Names are globally unique. If a later key is created with
the same name as an existing entry in `KC`, it will overwrite
@ -629,7 +686,7 @@ def register_key_names(key, names=tuple()): # NOQA
return key
def make_key(code=None, names=tuple(), type=KEY_SIMPLE, **kwargs): # NOQA
def make_key(code: Optional[int] = None, names Tuple[str, ...] = tuple(), type: int = KEY_SIMPLE, **kwargs): # NOQA
'''
Create a new key, aliased by `names` in the KC lookup table.