diff --git a/Pipfile b/Pipfile index a735877..9d44681 100644 --- a/Pipfile +++ b/Pipfile @@ -21,4 +21,6 @@ black = "==21.6b0" flake8-quotes = "*" flake8-black = "*" circuitpython-stubs = "==7.0.0a6.dev195" +pyright = "*" +typing = "*" mypy = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 41d222f..0c41bbb 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "e80d827684ac8c56b762a74548440c5b371c5b3ee5fcb5b74e68ae6d9bff0665" + "sha256": "cee0eeba8c8dad66dccffe0935656829132f7ca928569e3aa957f278e6e92da6" }, "pipfile-spec": 6, "requires": {}, @@ -94,11 +94,11 @@ }, "flake8-comprehensions": { "hashes": [ - "sha256:b07aef3277623db32310aa241a1cec67212b53c1d18e767d7e26d4d83aa05bf7", - "sha256:f24be9032587127f7a5bc6d066bf755b6e66834f694383adb8a673e229c1f559" + "sha256:4888de89248b7f7535159189ff693c77f8354f6d37a02619fa28c9921a913aa0", + "sha256:e9a010b99aa90c05790d45281ad9953df44a4a08a1a8f6cd41f98b4fc6a268a0" ], "index": "pypi", - "version": "==3.5.0" + "version": "==3.6.1" }, "flake8-isort": { "hashes": [ @@ -110,10 +110,10 @@ }, "flake8-quotes": { "hashes": [ - "sha256:3f1116e985ef437c130431ac92f9b3155f8f652fda7405ac22ffdfd7a9d1055e" + "sha256:f1dd87830ed77ff2ce47fc0ee0fd87ae20e8f045355354ffbf4dcaa18d528217" ], "index": "pypi", - "version": "==3.2.0" + "version": "==3.3.0" }, "greenlet": { "hashes": [ @@ -300,6 +300,13 @@ "index": "pypi", "version": "==0.3.1" }, + "nodeenv": { + "hashes": [ + "sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b", + "sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7" + ], + "version": "==1.6.0" + }, "parso": { "hashes": [ "sha256:12b83492c6239ce32ff5eed6d3639d6a536170723c6f3f1506869f1ace413398", @@ -332,11 +339,11 @@ }, "prompt-toolkit": { "hashes": [ - "sha256:08360ee3a3148bdb5163621709ee322ec34fc4375099afa4bbf751e9b7b7fa4f", - "sha256:7089d8d2938043508aa9420ec18ce0922885304cddae87fb96eebca942299f88" + "sha256:6076e46efae19b1e0ca1ec003ed37a933dc94b4d20f486235d436e64771dcd5c", + "sha256:eb71d5a6b72ce6db177af4a7d4d7085b99756bf656d98ffcc4fecd36850eea6c" ], - "markers": "python_full_version >= '3.6.1'", - "version": "==3.0.19" + "markers": "python_full_version >= '3.6.2'", + "version": "==3.0.20" }, "ptyprocess": { "hashes": [ @@ -363,11 +370,11 @@ }, "pygments": { "hashes": [ - "sha256:a18f47b506a429f6f4b9df81bb02beab9ca21d0a5fee38ed15aef65f0545519f", - "sha256:d66e804411278594d764fc69ec36ec13d9ae9147193a1740cd34d272ca383b8e" + "sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380", + "sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6" ], "markers": "python_version >= '3.5'", - "version": "==2.9.0" + "version": "==2.10.0" }, "pynvim": { "hashes": [ @@ -375,6 +382,14 @@ ], "version": "==0.4.3" }, + "pyright": { + "hashes": [ + "sha256:dd8e18c54321340be44a708b6037c0b967486c32b3f492741fffdc205cb82f15", + "sha256:e2668730cddf580e696d4a11946e740e2f5647df1eb45f7c55b7029376eac5a1" + ], + "index": "pypi", + "version": "==0.0.9" + }, "pyserial": { "hashes": [ "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb", @@ -408,41 +423,49 @@ }, "regex": { "hashes": [ - "sha256:026beb631097a4a3def7299aa5825e05e057de3c6d72b139c37813bfa351274b", - "sha256:14caacd1853e40103f59571f169704367e79fb78fac3d6d09ac84d9197cadd16", - "sha256:16d9eaa8c7e91537516c20da37db975f09ac2e7772a0694b245076c6d68f85da", - "sha256:18fdc51458abc0a974822333bd3a932d4e06ba2a3243e9a1da305668bd62ec6d", - "sha256:28e8af338240b6f39713a34e337c3813047896ace09d51593d6907c66c0708ba", - "sha256:3835de96524a7b6869a6c710b26c90e94558c31006e96ca3cf6af6751b27dca1", - "sha256:3905c86cc4ab6d71635d6419a6f8d972cab7c634539bba6053c47354fd04452c", - "sha256:3c09d88a07483231119f5017904db8f60ad67906efac3f1baa31b9b7f7cca281", - "sha256:4551728b767f35f86b8e5ec19a363df87450c7376d7419c3cac5b9ceb4bce576", - "sha256:459bbe342c5b2dec5c5223e7c363f291558bc27982ef39ffd6569e8c082bdc83", - "sha256:4f421e3cdd3a273bace013751c345f4ebeef08f05e8c10757533ada360b51a39", - "sha256:577737ec3d4c195c4aef01b757905779a9e9aee608fa1cf0aec16b5576c893d3", - "sha256:57fece29f7cc55d882fe282d9de52f2f522bb85290555b49394102f3621751ee", - "sha256:7976d410e42be9ae7458c1816a416218364e06e162b82e42f7060737e711d9ce", - "sha256:85f568892422a0e96235eb8ea6c5a41c8ccbf55576a2260c0160800dbd7c4f20", - "sha256:8764a78c5464ac6bde91a8c87dd718c27c1cabb7ed2b4beaf36d3e8e390567f9", - "sha256:8935937dad2c9b369c3d932b0edbc52a62647c2afb2fafc0c280f14a8bf56a6a", - "sha256:8fe58d9f6e3d1abf690174fd75800fda9bdc23d2a287e77758dc0e8567e38ce6", - "sha256:937b20955806381e08e54bd9d71f83276d1f883264808521b70b33d98e4dec5d", - "sha256:9569da9e78f0947b249370cb8fadf1015a193c359e7e442ac9ecc585d937f08d", - "sha256:a3b73390511edd2db2d34ff09aa0b2c08be974c71b4c0505b4a048d5dc128c2b", - "sha256:a4eddbe2a715b2dd3849afbdeacf1cc283160b24e09baf64fa5675f51940419d", - "sha256:a5c6dbe09aff091adfa8c7cfc1a0e83fdb8021ddb2c183512775a14f1435fe16", - "sha256:b63e3571b24a7959017573b6455e05b675050bbbea69408f35f3cb984ec54363", - "sha256:bb350eb1060591d8e89d6bac4713d41006cd4d479f5e11db334a48ff8999512f", - "sha256:bf6d987edd4a44dd2fa2723fca2790f9442ae4de2c8438e53fcb1befdf5d823a", - "sha256:bfa6a679410b394600eafd16336b2ce8de43e9b13f7fb9247d84ef5ad2b45e91", - "sha256:c856ec9b42e5af4fe2d8e75970fcc3a2c15925cbcc6e7a9bcb44583b10b95e80", - "sha256:cea56288eeda8b7511d507bbe7790d89ae7049daa5f51ae31a35ae3c05408531", - "sha256:ea212df6e5d3f60341aef46401d32fcfded85593af1d82b8b4a7a68cd67fdd6b", - "sha256:f35567470ee6dbfb946f069ed5f5615b40edcbb5f1e6e1d3d2b114468d505fc6", - "sha256:fbc20975eee093efa2071de80df7f972b7b35e560b213aafabcec7c0bd00bd8c", - "sha256:ff4a8ad9638b7ca52313d8732f37ecd5fd3c8e3aff10a8ccb93176fd5b3812f6" + "sha256:0696eb934dee723e3292056a2c046ddb1e4dd3887685783a9f4af638e85dee76", + "sha256:105122fa63da98d8456d5026bc6ac5a1399fd82fa6bad22c6ea641b1572c9142", + "sha256:116c277774f84266044e889501fe79cfd293a8b4336b7a5e89b9f20f1e5a9f21", + "sha256:12eaf0bbe568bd62e6cade7937e0bf01a2a4cef49a82f4fd204401e78409e158", + "sha256:1401cfa4320691cbd91191ec678735c727dee674d0997b0902a5a38ad482faf5", + "sha256:19acdb8831a4e3b03b23369db43178d8fee1f17b99c83af6cd907886f76bd9d4", + "sha256:208851a2f8dd31e468f0b5aa6c94433975bd67a107a4e7da3bdda947c9f85e25", + "sha256:24d68499a27b2d93831fde4a9b84ea5b19e0ab141425fbc9ab1e5b4dad179df7", + "sha256:2778c6cb379d804e429cc8e627392909e60db5152b42c695c37ae5757aae50ae", + "sha256:2a0a5e323cf86760784ce2b91d8ab5ea09d0865d6ef4da0151e03d15d097b24e", + "sha256:2d9cbe0c755ab8b6f583169c0783f7278fc6b195e423b09c5a8da6f858025e96", + "sha256:2de1429e4eeab799c168a4f6e6eecdf30fcaa389bba4039cc8a065d6b7aad647", + "sha256:32753eda8d413ce4f208cfe01dd61171a78068a6f5d5f38ccd751e00585cdf1d", + "sha256:3ee8ad16a35c45a5bab098e39020ecb6fec3b0e700a9d88983d35cbabcee79c8", + "sha256:4f03fc0a25122cdcbf39136510d4ea7627f732206892db522adf510bc03b8c67", + "sha256:4f3e36086d6631ceaf468503f96a3be0d247caef0660c9452fb1b0c055783851", + "sha256:503c1ba0920a46a1844363725215ef44d59fcac2bd2c03ae3c59aa9d08d29bd6", + "sha256:507861cf3d97a86fbe26ea6cc04660ae028b9e4080b8290e28b99547b4e15d89", + "sha256:56ae6e3cf0506ec0c40b466e31f41ee7a7149a2b505ae0ee50edd9043b423d27", + "sha256:6530b7b9505123cdea40a2301225183ca65f389bc6129f0c225b9b41680268d8", + "sha256:6729914dd73483cd1c8aaace3ac082436fc98b0072743ac136eaea0b3811d42f", + "sha256:7406dd2e44c7cfb4680c0a45a03264381802c67890cf506c147288f04c67177d", + "sha256:7684016b73938ca12d160d2907d141f06b7597bd17d854e32bb7588be01afa1d", + "sha256:7db58ad61f3f6ea393aaf124d774ee0c58806320bc85c06dc9480f5c7219c250", + "sha256:83946ca9278b304728b637bc8d8200ab1663a79de85e47724594917aeed0e892", + "sha256:84057cfae5676f456b03970eb78b7e182fddc80c2daafd83465a3d6ca9ff8dbf", + "sha256:862b6164e9a38b5c495be2c2854e75fd8af12c5be4c61dc9b42d255980d7e907", + "sha256:8ddb4f9ce6bb388ecc97b4b3eb37e786f05d7d5815e8822e0d87a3dbd7100649", + "sha256:92eb03f47427fea452ff6956d11f5d5a3f22a048c90a0f34fa223e6badab6c85", + "sha256:a5f3bc727fea58f21d99c22e6d4fca652dc11dbc2a1e7cfc4838cd53b2e3691f", + "sha256:a6180dbf5945b27e9420e1b58c3cacfc79ad5278bdad3ea35109f5680fbe16d1", + "sha256:b158f673ae6a6523f13704f70aa7e4ce875f91e379bece4362c89db18db189d5", + "sha256:cd45b4542134de63e7b9dd653e0a2d7d47ffed9615e3637c27ca5f6b78ea68bb", + "sha256:d2404336fd16788ea757d4218a2580de60adb052d9888031e765320be8884309", + "sha256:db888d4fb33a2fd54b57ac55d5015e51fa849f0d8592bd799b4e47f83bd04e00", + "sha256:dde0ac721c7c5bfa5f9fc285e811274dec3c392f2c1225f7d07ca98a8187ca84", + "sha256:de0d06ccbc06af5bf93bddec10f4f80275c5d74ea6d28b456931f3955f58bc8c", + "sha256:e02dad60e3e8442eefd28095e99b2ac98f2b8667167493ac6a2f3aadb5d84a17", + "sha256:e960fe211496333b2f7e36badf4c22a919d740386681f79139ee346b403d1ca1", + "sha256:e9700c52749cb3e90c98efd72b730c97b7e4962992fca5fbcaf1363be8e3b849", + "sha256:ee318974a1fdacba1701bc9e552e9015788d6345416364af6fa987424ff8df53" ], - "version": "==2021.8.3" + "version": "==2021.8.27" }, "s3cmd": { "hashes": [ @@ -462,10 +485,10 @@ }, "testfixtures": { "hashes": [ - "sha256:9bddf79b2dddb36420a20c25a65c827a8e7398c6ed4e2c75c2697857cb006be9", - "sha256:d4bd1c4f90eac90a73e1bdc59c31d03943f218d687f3c5a09e48478841a8af5f" + "sha256:0a6422737f6d89b45cdef1e2df5576f52ad0f507956002ce1020daa9f44211d6", + "sha256:486be7b01eb71326029811878a3317b7e7994324621c0ec633c8e24499d8d5b3" ], - "version": "==6.18.0" + "version": "==6.18.1" }, "toml": { "hashes": [ @@ -483,6 +506,14 @@ "markers": "python_version >= '3.7'", "version": "==5.0.5" }, + "typing": { + "hashes": [ + "sha256:1187fb9c82fd670d10aa07bbb6cfcfe4bdda42d6fab8d5134f04e8c4d0b71cc9", + "sha256:283d868f5071ab9ad873e5e52268d611e851c870a2ba354193026f2dfb29d8b5" + ], + "index": "pypi", + "version": "==3.7.4.3" + }, "typing-extensions": { "hashes": [ "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497", diff --git a/kmk/consts.py b/kmk/consts.py index b222ede..62bc121 100644 --- a/kmk/consts.py +++ b/kmk/consts.py @@ -11,3 +11,17 @@ class UnicodeMode: LINUX = IBUS = const(1) MACOS = OSX = RALT = const(2) WINC = const(3) + + +TYPING_PLATFORMS = [ + 'linux', + 'linux2', + 'win32', + 'cygwin', + 'msys', + 'darwin', + 'freebsd7', + 'freebsd8', + 'freebsdN', + 'openbsd6', +] diff --git a/kmk/extensions/__init__.py b/kmk/extensions/__init__.py index 1eba5e5..3f9dbdb 100644 --- a/kmk/extensions/__init__.py +++ b/kmk/extensions/__init__.py @@ -1,16 +1,21 @@ +from kmk.kmk_keyboard import KMKKeyboard + + class InvalidExtensionEnvironment(Exception): pass class Extension: - _enabled = True + _enabled = True # type: bool def enable(self, keyboard): + # type: (KMKKeyboard) -> None self._enabled = True self.on_runtime_enable(keyboard) def disable(self, keyboard): + # type (KMKKeyboard) -> None self._enabled = False self.on_runtime_disable(keyboard) @@ -18,34 +23,43 @@ class Extension: # The below methods should be implemented by subclasses def on_runtime_enable(self, keyboard): + # type: (KMKKeyboard) -> None raise NotImplementedError def on_runtime_disable(self, keyboard): + # type: (KMKKeyboard) -> None raise NotImplementedError def during_bootup(self, keyboard): + # type: (KMKKeyboard) -> None raise NotImplementedError def before_matrix_scan(self, keyboard): + # type: (KMKKeyboard) -> None ''' Return value will be injected as an extra matrix update ''' raise NotImplementedError def after_matrix_scan(self, keyboard): + # type: (KMKKeyboard) -> None ''' Return value will be replace matrix update if supplied ''' raise NotImplementedError def before_hid_send(self, keyboard): + # type: (KMKKeyboard) -> None raise NotImplementedError def after_hid_send(self, keyboard): + # type: (KMKKeyboard) -> None raise NotImplementedError def on_powersave_enable(self, keyboard): + # type: (KMKKeyboard) -> None raise NotImplementedError def on_powersave_disable(self, keyboard): + # type: (KMKKeyboard) -> None raise NotImplementedError diff --git a/kmk/extensions/international.py b/kmk/extensions/international.py index eab8087..bb6aedb 100644 --- a/kmk/extensions/international.py +++ b/kmk/extensions/international.py @@ -1,6 +1,7 @@ '''Adds international keys''' from kmk.extensions import Extension from kmk.keys import make_key +from kmk.kmk_keyboard import KMKKeyboard class International(Extension): @@ -32,28 +33,37 @@ class International(Extension): make_key(code=152, names=('LANG9',)) def on_runtime_enable(self, sandbox): + # type: (KMKKeyboard) -> None return def on_runtime_disable(self, sandbox): + # type: (KMKKeyboard) -> None return def during_bootup(self, sandbox): + # type: (KMKKeyboard) -> None return def before_matrix_scan(self, sandbox): + # type: (KMKKeyboard) -> None return def after_matrix_scan(self, sandbox): + # type: (KMKKeyboard) -> None return def before_hid_send(self, sandbox): + # type: (KMKKeyboard) -> None return def after_hid_send(self, sandbox): + # type: (KMKKeyboard) -> None return def on_powersave_enable(self, sandbox): + # type: (KMKKeyboard) -> None return def on_powersave_disable(self, sandbox): + # type: (KMKKeyboard) -> None return diff --git a/kmk/handlers/stock.py b/kmk/handlers/stock.py index 515e5ba..b145133 100644 --- a/kmk/handlers/stock.py +++ b/kmk/handlers/stock.py @@ -1,23 +1,29 @@ -from typing import Any, Optional +import sys -from kmk.keys import Key, KeyAttrDict -from kmk.kmk_keyboard import KMKKeyboard +from kmk.consts import TYPING_PLATFORMS from kmk.kmktime import sleep_ms +if sys.platform in TYPING_PLATFORMS: + from typing import Any, Optional + + from kmk.keys import Key, KeyAttrDict + from kmk.kmk_keyboard import KMKKeyboard # Avoid cyclical imports + def passthrough(key, keyboard, *args, **kwargs): return keyboard def default_pressed( - key: Key, - keyboard: KMKKeyboard, - KC: KeyAttrDict, - coord_int: Optional[int] = None, - coord_raw: Optional[str] = None, - *args: Any, - **kwargs: Any, -) -> KMKKeyboard: + key, # type: Key + keyboard, # type: KMKKeyboard + KC, # type: KeyAttrDict + coord_int=None, # type: Optional[int] + coord_raw=None, # type: Optional[str] + *args, # type: Any + **kwargs, # type: Any +): + # type: (...) -> KMKKeyboard keyboard.hid_pending = True if coord_int is not None: @@ -29,14 +35,15 @@ def default_pressed( def default_released( - key: Key, - keyboard: KMKKeyboard, - KC: KeyAttrDict, - coord_int: Optional[int] = None, - coord_raw: Optional[str] = None, - *args: Any, - **kwargs: Any, # NOQA + key, # type: Key + keyboard, # type: KMKKeyboard + KC, # type: KeyAttrDict + coord_int=None, # type: Optional[int] + coord_raw=None, # type: Optional[str] + *args, # type: Any + **kwargs, # type: Any # NOQA ): + # type: (...) -> KMKKeyboard keyboard.hid_pending = True keyboard.keys_pressed.discard(key) diff --git a/kmk/key_validators.py b/kmk/key_validators.py index 651a8c8..b079497 100644 --- a/kmk/key_validators.py +++ b/kmk/key_validators.py @@ -1,6 +1,6 @@ -from typing import List, Optional +import sys -from kmk.keys import Key +from kmk.consts import TYPING_PLATFORMS from kmk.types import ( KeySeqSleepMeta, LayerKeyMeta, @@ -9,12 +9,20 @@ from kmk.types import ( UnicodeModeKeyMeta, ) +if sys.platform in TYPING_PLATFORMS: + from typing import List, Optional -def key_seq_sleep_validator(ms: float) -> KeySeqSleepMeta: + # Avoid cyclical imports + from kmk.keys import Key + + +def key_seq_sleep_validator(ms): + # type: (float) -> KeySeqSleepMeta return KeySeqSleepMeta(ms) -def layer_key_validator(layer: int, kc: Key = None) -> LayerKeyMeta: +def layer_key_validator(layer, kc=None): + # type: (int, Optional[Key]) -> LayerKeyMeta ''' Validates the syntax (but not semantics) of a layer key call. We won't have access to the keymap here, so we can't verify much of anything useful @@ -25,16 +33,19 @@ def layer_key_validator(layer: int, kc: Key = None) -> LayerKeyMeta: return LayerKeyMeta(layer=layer, kc=kc) -def mod_tap_validator(kc: Key, mods: Optional[List[Key]] = None) -> ModTapKeyMeta: +def mod_tap_validator(kc, mods): + # type: (Key, Optional[List[Key]]) -> ModTapKeyMeta ''' Validates that mod tap keys are correctly used ''' return ModTapKeyMeta(kc=kc, mods=mods) -def tap_dance_key_validator(*codes: Key) -> TapDanceKeyMeta: +def tap_dance_key_validator(*codes): + # type: (*Key) -> TapDanceKeyMeta return TapDanceKeyMeta(codes) -def unicode_mode_key_validator(mode: int) -> UnicodeModeKeyMeta: +def unicode_mode_key_validator(mode): + # type: (int) -> UnicodeModeKeyMeta return UnicodeModeKeyMeta(mode) diff --git a/kmk/keys.py b/kmk/keys.py index 92e5f3e..858faf1 100644 --- a/kmk/keys.py +++ b/kmk/keys.py @@ -1,43 +1,37 @@ -from __future__ import annotations - import gc from micropython import const -from typing import Any, Callable, List, Optional, Protocol, Set, Tuple, Union +import sys import kmk.handlers.stock as handlers -from kmk.consts import UnicodeMode +from kmk.consts import TYPING_PLATFORMS, UnicodeMode from kmk.key_validators import ( key_seq_sleep_validator, tap_dance_key_validator, unicode_mode_key_validator, ) -from kmk.kmk_keyboard import KMKKeyboard from kmk.types import AttrDict, UnicodeModeKeyMeta -DEBUG_OUTPUT: bool = False +if sys.platform in TYPING_PLATFORMS: + from typing import Any, Callable, List, Optional, Set, Tuple, Union -FIRST_KMK_INTERNAL_KEY: int = const(1000) -NEXT_AVAILABLE_KEY: int = 1000 + # Avoid cyclical imports + from kmk.kmk_keyboard import KMKKeyboard -KEY_SIMPLE: int = const(0) -KEY_MODIFIER: int = const(1) -KEY_CONSUMER: int = const(2) -ALL_ALPHAS: str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' -ALL_NUMBERS: str = '1234567890' +DEBUG_OUTPUT = False # type: bool + +FIRST_KMK_INTERNAL_KEY = const(1000) # type: int +NEXT_AVAILABLE_KEY = 1000 # type: int + +KEY_SIMPLE = const(0) # type: int +KEY_MODIFIER = const(1) # type: int +KEY_CONSUMER = const(2) # type: int + +ALL_ALPHAS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' # type: str +ALL_NUMBERS = '1234567890' # type: str # since KC.1 isn't valid Python, alias to KC.N1 -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: - ... +ALL_NUMBER_ALIASES = tuple(f'N{x}' for x in ALL_NUMBERS) # type: Tuple[str, ...] class InfiniteLoopDetected(Exception): @@ -47,10 +41,11 @@ 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: str, - functor: LeftPipeCallback, - *args_iter: Tuple[Union[str, int], Tuple[str, ...]], -) -> KeyReturn: + candidate, # type: str + functor, # type: Callable[[str, Union[str, int], Tuple[str, ...]], Optional[Union[Key, ModifierKey, ConsumerKey]]] + *args_iter, # type: Tuple[Union[str, int], Tuple[str, ...]] +): + # type: (...) -> Optional[Union[Key, ModifierKey, ConsumerKey]] for args in args_iter: result = functor(candidate, *args) if result is not None: @@ -58,41 +53,43 @@ def left_pipe_until_some( def first_truthy( - candidate: str, - *funcs: Callable[[str], Union[Key, ModifierKey, ConsumerKey, None]], -) -> KeyReturn: + candidate, # type: str + *funcs, # type: Callable[[str], Union[Key, ModifierKey, ConsumerKey, None]] +): + # type: (...) -> Optional[Union[Key, ModifierKey, ConsumerKey]] for func in funcs: result = func(candidate) if result is not None: return result -def maybe_make_mod_key(candidate: str, code: int, names: Tuple[str, ...]) -> KeyReturn: +def maybe_make_mod_key(candidate, code, names): + # type: (str, int, Tuple[str, ...]) -> Optional[Union[Key, ModifierKey, ConsumerKey]] if candidate in names: return make_mod_key(code=code, names=names) -def maybe_make_key(candidate: str, code: int, names: Tuple[str, ...]) -> KeyReturn: +def maybe_make_key(candidate, code, names): + # type: (str, int, Tuple[str, ...]) -> Optional[Union[Key, ModifierKey, ConsumerKey]] if candidate in names: return make_key(code=code, names=names) -def maybe_make_shifted_key( - candidate: str, target_name: str, names: Tuple[str, ...] -) -> KeyReturn: +def maybe_make_shifted_key(candidate, target_name, names): + # type: (str, str, Tuple[str, ...]) -> Optional[Union[Key, ModifierKey, ConsumerKey]] if candidate in names: return make_shifted_key(target_name=target_name, names=names) -def maybe_make_consumer_key( - candidate: str, code: int, names: Tuple[str, ...] -) -> KeyReturn: +def maybe_make_consumer_key(candidate, code, names): + # type: (str, int, Tuple[str, ...]) -> Optional[Union[Key, ModifierKey, ConsumerKey]] if candidate in names: return make_consumer_key(code=code, names=names) class KeyAttrDict(AttrDict): - def __getattr__(self, key: str, depth: int = 0) -> KeyReturn: + def __getattr__(self, key, depth=0): + # type: (str, int) -> Optional[Union[Key, ModifierKey, ConsumerKey]] if depth > 1: raise InfiniteLoopDetected() @@ -410,54 +407,46 @@ 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: 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 + code, # type: int + has_modifiers=None, # type: Optional[Set[int]] + no_press=False, # type: bool + no_release=False, # type: bool + on_press=handlers.default_pressed, # type: Callable[[Key, KMKKeyboard, KeyAttrDict, Optional[int], Optional[str], Any, Any], KMKKeyboard] + on_release=handlers.default_released, # type: Callable[[Key, KMKKeyboard, KeyAttrDict, Optional[int], Optional[str], Any, Any], KMKKeyboard] + meta=object(), # type: object + ): + # type: (...) -> None + self.code = code # type: int + self.has_modifiers = has_modifiers # type: Optional[Set[int]] # cast to bool() in case we get a None value - self.no_press: bool = bool(no_press) - self.no_release: bool = bool(no_press) + self.no_press = bool(no_press) # type: bool + self.no_release = bool(no_press) # type: bool - 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 + self._pre_press_handlers = ( + [] + ) # type: List[Callable[[Any, KMKKeyboard, KeyAttrDict, int, str], KMKKeyboard]] + self._post_press_handlers = ( + [] + ) # type: List[Callable[[Any, KMKKeyboard, KeyAttrDict, int, str], KMKKeyboard]] + self._pre_release_handlers = ( + [] + ) # type: List[Callable[[Any, KMKKeyboard, KeyAttrDict, int, str], KMKKeyboard]] + self._post_release_handlers = ( + [] + ) # type: List[Callable[[Any, KMKKeyboard, KeyAttrDict, int, str], KMKKeyboard]] + self._handle_press = ( + on_press + ) # type: Callable[[Key, KMKKeyboard, KeyAttrDict, Optional[int], Optional[str], Any, Any], KMKKeyboard] + self._handle_release = ( + on_release + ) # type: Callable[[Key, KMKKeyboard, KeyAttrDict, Optional[int], Optional[str], Any, Any], KMKKeyboard] + self.meta = meta # type: object - def __call__( - self, no_press: Optional[bool] = None, no_release: Optional[bool] = None - ) -> Key: + def __call__(self, no_press, no_release): + # type: (Optional[bool], Optional[bool]) -> Key if no_press is None and no_release is None: return self @@ -469,37 +458,43 @@ class Key: ) def __repr__(self): + # type: () -> str return 'Key(code={}, has_modifiers={})'.format(self.code, self.has_modifiers) - def on_press( - self, state: KMKKeyboard, coord_int: int, coord_raw: str - ) -> Union[KMKKeyboard, None]: + def on_press(self, state, coord_int, coord_raw): + # type: (KMKKeyboard, int, str) -> Optional[Callable[[Key, KMKKeyboard, KeyAttrDict, Optional[int], Optional[str], Any, Any], KMKKeyboard]] for fn in self._pre_press_handlers: if not fn(self, state, KC, coord_int, coord_raw): return None - ret = self._handle_press(self, state, KC, coord_int, coord_raw) + # TODO -- SOFUBI -- look into way to type hint *args and **kwargs of a Callable signature + ret = self._handle_press( + self, state, KC, coord_int, coord_raw + ) # type: Callable[[Key, KMKKeyboard, KeyAttrDict, Optional[int], Optional[str], Any, Any], KMKKeyboard] for fn in self._post_press_handlers: fn(self, state, KC, coord_int, coord_raw) return ret - def on_release( - self, state: KMKKeyboard, coord_int: int, coord_raw: str - ) -> Union[KMKKeyboard, None]: + def on_release(self, state, coord_int, coord_raw): + # type: (KMKKeyboard, int, str) -> Optional[Callable[[Key, KMKKeyboard, KeyAttrDict, Optional[int], Optional[str], Any, Any], KMKKeyboard]] for fn in self._pre_release_handlers: if not fn(self, state, KC, coord_int, coord_raw): return None - ret = self._handle_release(self, state, KC, coord_int, coord_raw) + # TODO -- SOFUBI -- look into way to type hint *args and **kwargs of a Callable signature + ret = self._handle_release( + self, state, KC, coord_int, coord_raw + ) # type: Callable[[Key, KMKKeyboard, KeyAttrDict, Optional[int], Optional[str], Any, Any], KMKKeyboard] for fn in self._post_release_handlers: fn(self, state, KC, coord_int, coord_raw) return ret - def clone(self) -> Key: + def clone(self): + # type: () -> 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 @@ -516,7 +511,8 @@ class Key: meta=self.meta, ) - def before_press_handler(self, fn: Handler) -> Key: + def before_press_handler(self, fn): + # type: (Callable[[Any, KMKKeyboard, KeyAttrDict, int, str], KMKKeyboard]) -> Key ''' Attach a callback to be run prior to the on_press handler for this key. Receives the following: @@ -540,7 +536,8 @@ class Key: self._pre_press_handlers.append(fn) return self - def after_press_handler(self, fn: Handler) -> Key: + def after_press_handler(self, fn): + # type: (Callable[[Any, KMKKeyboard, KeyAttrDict, int, str], KMKKeyboard]) -> Key ''' Attach a callback to be run after the on_release handler for this key. Receives the following: @@ -563,7 +560,8 @@ class Key: self._post_press_handlers.append(fn) return self - def before_release_handler(self, fn: Handler) -> Key: + def before_release_handler(self, fn): + # type: (Callable[[Any, KMKKeyboard, KeyAttrDict, int, str], KMKKeyboard]) -> Key ''' Attach a callback to be run prior to the on_release handler for this key. Receives the following: @@ -587,7 +585,8 @@ class Key: self._pre_release_handlers.append(fn) return self - def after_release_handler(self, fn: Handler) -> Key: + def after_release_handler(self, fn): + # type: (Callable[[Any, KMKKeyboard, KeyAttrDict, int, str], KMKKeyboard]) -> Key ''' Attach a callback to be run after the on_release handler for this key. Receives the following: @@ -615,14 +614,15 @@ class ModifierKey(Key): # FIXME this is atrocious to read. Please, please, please, strike down upon # this with great vengeance and furious anger. - FAKE_CODE: int = const(-1) + FAKE_CODE = const(-1) # type: int def __call__( self, - modified_code: Optional[Key] = None, - no_press: Optional[bool] = None, - no_release: Optional[bool] = None, - ) -> Union[Key, ModifierKey]: + modified_code=None, # type: Optional[Key] + no_press=None, # type: Optional[bool] + no_release=None, # type: Optional[bool] + ): + # type: (...) -> Union[Key, ModifierKey] if modified_code is None and no_press is None and no_release is None: return self @@ -655,7 +655,8 @@ class ModifierKey(Key): return new_keycode - def __repr__(self) -> str: + def __repr__(self): + # () -> str return 'ModifierKey(code={}, has_modifiers={})'.format( self.code, self.has_modifiers ) @@ -665,7 +666,8 @@ class ConsumerKey(Key): pass -def register_key_names(key: Key, names: Tuple[str, ...] = tuple()): # NOQA +def register_key_names(key, names=tuple()): # NOQA + # type: (Key, Tuple[str, ...]) -> Key ''' Names are globally unique. If a later key is created with the same name as an existing entry in `KC`, it will overwrite @@ -686,12 +688,8 @@ def register_key_names(key: Key, names: Tuple[str, ...] = tuple()): # NOQA return key -def make_key( - code: Optional[int] = None, - names: Tuple[str, ...] = tuple(), - type: int = KEY_SIMPLE, - **kwargs: Any, -) -> KeyReturn: # NOQA +def make_key(code=None, names=tuple(), type=KEY_SIMPLE, **kwargs): # NOQA + # type: (Optional[int], Tuple[str, ...], int, **Any) -> Optional[Union[Key, ModifierKey, ConsumerKey]] ''' Create a new key, aliased by `names` in the KC lookup table. @@ -733,16 +731,13 @@ def make_key( return key -def make_mod_key( - code: Optional[int], - names: Tuple[str, ...], - *args: Any, - **kwargs: Any, -): +def make_mod_key(code, names, *args, **kwargs): + # type: (Optional[int], Tuple[str, ...], *Any, **Any) -> Optional[Union[Key, ModifierKey, ConsumerKey]] return make_key(code, names, *args, **kwargs, type=KEY_MODIFIER) def make_shifted_key(target_name, names=tuple()): # NOQA + # type: (str, Tuple[str, ...]) -> Optional[Union[Key, ModifierKey, ConsumerKey]] # For... probably a few years, a bug existed here where keys were looked # up by `KC[...]`, but that's incorrect: an AttrDit exposes a dictionary # with attributes, but key-based dictionary access with brackets does @@ -757,34 +752,28 @@ def make_shifted_key(target_name, names=tuple()): # NOQA return key -def make_consumer_key(*args: Any, **kwargs: Any): +def make_consumer_key(*args, **kwargs): + # type: (*Any, **Any) -> Optional[Union[Key, ModifierKey, ConsumerKey]] return make_key(*args, **kwargs, type=KEY_CONSUMER) -class ArgumentedKey(Protocol): - def __call__(self, *user_args: Any, **user_kwargs: Any) -> Key: - ... - - -class Validator(Protocol): - def __call__(self, *validator_args: Any, **validator_kwargs: Any) -> object: - ... - - # Argumented keys are implicitly internal, so auto-gen of code # is almost certainly the best plan here def make_argumented_key( - validator: Validator = lambda *validator_args, **validator_kwargs: object(), - names: Tuple = tuple(), # NOQA - *constructor_args: Any, - **constructor_kwargs: Any, -) -> Optional[ArgumentedKey]: + validator=lambda *validator_args, **validator_kwargs: object(), # type: object + names=tuple(), # type: Tuple[str, ...] # NOQA + *constructor_args, # type; Any + **constructor_kwargs, # type: Any +): global NEXT_AVAILABLE_KEY - def _argumented_key(*user_args: Any, **user_kwargs: Any): + def _argumented_key(*user_args, **user_kwargs): + # type: (*Any, **Any) -> Union[Key, ModifierKey, ConsumerKey] global NEXT_AVAILABLE_KEY - meta = validator(*user_args, **user_kwargs) + meta = validator( + *user_args, **user_kwargs + ) # type: Callable[[Any, Any], object] if meta: key = Key( diff --git a/kmk/kmk_keyboard.py b/kmk/kmk_keyboard.py index d2af92d..f0aac9e 100644 --- a/kmk/kmk_keyboard.py +++ b/kmk/kmk_keyboard.py @@ -1,77 +1,80 @@ -from __future__ import annotations +import sys -from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type, Union - -from kmk.consts import KMK_RELEASE, UnicodeMode +from kmk.consts import KMK_RELEASE, TYPING_PLATFORMS, UnicodeMode from kmk.hid import BLEHID, USBHID, AbstractHID, HIDModes from kmk.keys import KC, Key, KeyAttrDict from kmk.kmktime import ticks_ms from kmk.matrix import MatrixScanner, intify_coordinate from kmk.types import TapDanceKeyMeta +if sys.platform in TYPING_PLATFORMS: + from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type, Union + class Sandbox: - matrix_update: Optional[bytearray] = None - secondary_matrix_update: Optional[bytearray] = None - active_layers: Optional[List[int]] = None + matrix_update = None # type: Optional[bytearray] + secondary_matrix_update = None # type: Optional[bytearray] + active_layers = None # type: Optional[List[int]] class KMKKeyboard: ##### # User-configurable - debug_enabled: bool = False + debug_enabled = False # type: bool - keymap: List[KeyAttrDict] = [] - coord_mapping: Optional[List[int]] = None + keymap = [] # type: List[KeyAttrDict] + coord_mapping = None # type: Optional[List[int]] - row_pins: Optional[Tuple[Any, ...]] = None - col_pins: Optional[Tuple[Any, ...]] = None - diode_orientation: Optional[int] = None - matrix: Optional[MatrixScanner] = None - matrix_scanner: Type[MatrixScanner] = MatrixScanner - uart_buffer: List[Any] = [] + row_pins = None # type: Optional[Tuple[Any, ...]] + col_pins = None # type: Optional[Tuple[Any, ...]] + diode_orientation = None # type: Optional[int] + matrix = None # type: Optional[MatrixScanner] + matrix_scanner = MatrixScanner # type: Type[MatrixScanner] + uart_buffer = [] # type: List[Any] - unicode_mode: int = UnicodeMode.NOOP - tap_time: int = 300 + unicode_mode = UnicodeMode.NOOP # type: int + tap_time = 300 # type: int - modules: List[Type[Any]] = [] - extensions: List[Type[Any]] = [] - sandbox: Sandbox = Sandbox() + modules = [] # type: List[Type[Any]] + extensions = [] # type: List[Type[Any]] + sandbox = Sandbox() # type: Sandbox ##### # Internal State - keys_pressed: Set[Key] = set() - _coordkeys_pressed: Dict[Any, Any] = {} - hid_type: int = HIDModes.USB - secondary_hid_type: Optional[int] = None - _hid_helper: Optional[Union[Type[AbstractHID], Type[BLEHID], Type[USBHID]]] = None - hid_pending: bool = False - state_layer_key: Optional[Key] = None - matrix_update: Optional[Union[bytearray, None]] = None - secondary_matrix_update: Optional[Union[bytearray, None]] = None - _matrix_modify: Optional[Any] = None - state_changed: bool = False - _old_timeouts_len: Optional[int] = None - _new_timeouts_len: Optional[int] = None - _trigger_powersave_enable: bool = False - _trigger_powersave_disable: bool = False - i2c_deinit_count: int = 0 + keys_pressed = set() # type: Set[Key] + _coordkeys_pressed = {} # type: Dict[Any, Any] + hid_type = HIDModes.USB # type: int + secondary_hid_type = None # type: Optional[int] + _hid_helper = ( + None + ) # type: Optional[Union[Type[AbstractHID], Type[BLEHID], Type[USBHID]]] + hid_pending = False # type: bool + state_layer_key = None # type: Optional[Key] + matrix_update = None # type: Optional[Union[bytearray, None]] + secondary_matrix_update = None # type: Optional[Union[bytearray, None]] + _matrix_modify = None # type: Optional[Any] + state_changed = False # type: bool + _old_timeouts_len = None # type: Optional[int] + _new_timeouts_len = None # type: Optional[int] + _trigger_powersave_enable = False # type: bool + _trigger_powersave_disable = False # type: bool + i2c_deinit_count = 0 # type: int # this should almost always be PREpended to, replaces # former use of reversed_active_layers which had pointless # overhead (the underlying list was never used anyway) - active_layers: List[int] = [0] + active_layers = [0] # type: List[int] - _timeouts: Dict[float, Callable[[], Key]] = {} - _tapping: bool = False - _tap_dance_counts: Dict[Union[Key, TapDanceKeyMeta], Union[int, None]] = {} - _tap_side_effects: Dict[Union[Key, TapDanceKeyMeta], Union[Key, None]] = {} + _timeouts = {} # type: Dict[float, Callable[[], Key]] + _tapping = False # type: bool + _tap_dance_counts = {} # type: Dict[Union[Key, TapDanceKeyMeta], Union[int, None]] + _tap_side_effects = {} # type: Dict[Union[Key, TapDanceKeyMeta], Union[int, None]] # on some M4 setups (such as klardotsh/klarank_feather_m4, CircuitPython # 6.0rc1) this runs out of RAM every cycle and takes down the board. no # real known fix yet other than turning off debug, but M4s have always been # tight on RAM so.... - def __repr__(self) -> str: + def __repr__(self): # type: () -> str return ( 'KMKKeyboard(' 'debug_enabled={} ' @@ -107,22 +110,26 @@ class KMKKeyboard: self._tap_side_effects, ) - def _print_debug_cycle(self, init: bool = False) -> None: + def _print_debug_cycle(self, init=False): + # type: (bool) -> None if self.debug_enabled: if init: print('KMKInit(release={})'.format(KMK_RELEASE)) print(self) - def _send_hid(self) -> None: + def _send_hid(self): + # type: () -> None self._hid_helper.create_report(self.keys_pressed).send() self.hid_pending = False - def _handle_matrix_report(self, update: bytearray = None) -> None: + def _handle_matrix_report(self, update=None): + # type: (Optional[bytearray]) -> None if update is not None: self._on_matrix_changed(update[0], update[1], update[2]) self.state_changed = True - def _find_key_in_map(self, int_coord: int, row: int, col: int) -> Union[Key, None]: + def _find_key_in_map(self, int_coord, row, col): + # type: (int, int, int) -> Optional[Key] self.state_layer_key = None try: idx = self.coord_mapping.index(int_coord) @@ -147,7 +154,8 @@ class KMKKeyboard: return self.state_layer_key - def _on_matrix_changed(self, row: int, col: int, is_pressed: int) -> KMKKeyboard: + def _on_matrix_changed(self, row, col, is_pressed): + # type: (int, int, int) -> KMKKeyboard if self.debug_enabled: print('MatrixChange(col={} row={} pressed={})'.format(col, row, is_pressed)) @@ -162,11 +170,12 @@ class KMKKeyboard: def process_key( self, - key: Union[Key, TapDanceKeyMeta], - is_pressed: int, - coord_int: Optional[int] = None, - coord_raw: Tuple[int, int] = None, - ) -> KMKKeyboard: + key, # type: Union[Key, TapDanceKeyMeta] + is_pressed, # type: int + coord_int=None, # type: Optional[int] + coord_raw=None, # type: Optional[Tuple[int, int]] + ): + # (...) -> KMKKeyboard if self._tapping and not isinstance(key.meta, TapDanceKeyMeta): self._process_tap_dance(key, is_pressed) else: @@ -177,24 +186,26 @@ class KMKKeyboard: return self - def remove_key(self, keycode: Key) -> KMKKeyboard: + def remove_key(self, keycode): + # type: (Key) -> KMKKeyboard self.keys_pressed.discard(keycode) return self.process_key(keycode, False) - def add_key(self, keycode: Key) -> KMKKeyboard: + def add_key(self, keycode): + # type: (Key) -> KMKKeyboard self.keys_pressed.add(keycode) return self.process_key(keycode, True) - def tap_key(self, keycode: Key) -> KMKKeyboard: + def tap_key(self, keycode): + # type: (Key) -> KMKKeyboard self.add_key(keycode) # On the next cycle, we'll remove the key. self.set_timeout(False, lambda: self.remove_key(keycode)) return self - def _process_tap_dance( - self, changed_key: Union[Key, TapDanceKeyMeta], is_pressed: int - ) -> KMKKeyboard: + def _process_tap_dance(self, changed_key, is_pressed): + # type: (Union[Key, TapDanceKeyMeta], int) -> KMKKeyboard if is_pressed: if not isinstance(changed_key.meta, TapDanceKeyMeta): # If we get here, changed_key is not a TapDanceKey and thus @@ -231,7 +242,8 @@ class KMKKeyboard: return self - def _end_tap_dance(self, td_key: Union[Key, TapDanceKeyMeta]) -> KMKKeyboard: + def _end_tap_dance(self, td_key): + # type: (Union[Key, TapDanceKeyMeta]) -> KMKKeyboard v = self._tap_dance_counts[td_key] - 1 if v >= 0: @@ -252,12 +264,14 @@ class KMKKeyboard: return self - def _cleanup_tap_dance(self, td_key: Union[Key, TapDanceKeyMeta]) -> KMKKeyboard: + def _cleanup_tap_dance(self, td_key): + # type: (Union[Key, TapDanceKeyMeta]) -> KMKKeyboard self._tap_dance_counts[td_key] = 0 self._tapping = any(count > 0 for count in self._tap_dance_counts.values()) return self - def set_timeout(self, after_ticks: float, callback: Callable[[], Key]) -> float: + def set_timeout(self, after_ticks, callback): + # type: (float, Callable[[], Key]) -> float if after_ticks is False: # We allow passing False as an implicit "run this on the next process timeouts cycle" timeout_key = ticks_ms() @@ -270,11 +284,13 @@ class KMKKeyboard: self._timeouts[timeout_key] = callback return timeout_key - def _cancel_timeout(self, timeout_key: float) -> None: + def _cancel_timeout(self, timeout_key): + # type: (float) -> None if timeout_key in self._timeouts: del self._timeouts[timeout_key] - def _process_timeouts(self) -> KMKKeyboard: + def _process_timeouts(self): + # type: () -> KMKKeyboard if not self._timeouts: return self @@ -291,7 +307,8 @@ class KMKKeyboard: return self - def _init_sanity_check(self) -> KMKKeyboard: + def _init_sanity_check(self): + # type: () -> KMKKeyboard ''' Ensure the provided configuration is *probably* bootable ''' @@ -305,7 +322,8 @@ class KMKKeyboard: return self - def _init_coord_mapping(self) -> None: + def _init_coord_mapping(self): + # () -> None ''' Attempt to sanely guess a coord_mapping if one is not provided. No-op if `kmk.extensions.split.Split` is used, it provides equivalent @@ -327,7 +345,8 @@ class KMKKeyboard: for cidx in range(cols_to_calc): self.coord_mapping.append(intify_coordinate(ridx, cidx)) - def _init_hid(self) -> None: + def _init_hid(self): + # type: () -> None if self.hid_type == HIDModes.NOOP: self._hid_helper = AbstractHID elif self.hid_type == HIDModes.USB: @@ -338,7 +357,8 @@ class KMKKeyboard: self._hid_helper = AbstractHID self._hid_helper = self._hid_helper() - def _init_matrix(self) -> KMKKeyboard: + def _init_matrix(self): + # type: () -> KMKKeyboard self.matrix = MatrixScanner( cols=self.col_pins, rows=self.row_pins, @@ -348,7 +368,8 @@ class KMKKeyboard: return self - def before_matrix_scan(self) -> None: + def before_matrix_scan(self): + # type: () -> None for module in self.modules: try: module.before_matrix_scan(self) @@ -363,7 +384,8 @@ class KMKKeyboard: if self.debug_enabled: print('Failed to run pre matrix function in extension: ', err, ext) - def after_matrix_scan(self) -> None: + def after_matrix_scan(self): + # type: () -> None for module in self.modules: try: module.after_matrix_scan(self) @@ -378,7 +400,8 @@ class KMKKeyboard: if self.debug_enabled: print('Failed to run post matrix function in extension: ', err, ext) - def before_hid_send(self) -> None: + def before_hid_send(self): + # type: () -> None for module in self.modules: try: module.before_hid_send(self) @@ -393,7 +416,8 @@ class KMKKeyboard: if self.debug_enabled: print('Failed to run pre hid function in extension: ', err, ext) - def after_hid_send(self) -> None: + def after_hid_send(self): + # type: () -> None for module in self.modules: try: module.after_hid_send(self) @@ -408,7 +432,8 @@ class KMKKeyboard: if self.debug_enabled: print('Failed to run post hid function in extension: ', err, ext) - def powersave_enable(self) -> None: + def powersave_enable(self): + # type: () -> None for module in self.modules: try: module.on_powersave_enable(self) @@ -423,7 +448,8 @@ class KMKKeyboard: if self.debug_enabled: print('Failed to run post hid function in extension: ', err, ext) - def powersave_disable(self) -> None: + def powersave_disable(self): + # type: () -> None for module in self.modules: try: module.on_powersave_disable(self) @@ -439,10 +465,11 @@ class KMKKeyboard: def go( self, - hid_type: int = HIDModes.USB, - secondary_hid_type: Optional[int] = None, - **kwargs: Dict[Any, Any], - ) -> None: + hid_type=HIDModes.USB, # type: int + secondary_hid_type=None, # type: Optional[int] + **kwargs, # type: Dict[Any, Any] + ): + # (...) -> None self.hid_type = hid_type self.secondary_hid_type = secondary_hid_type diff --git a/kmk/kmktime.py b/kmk/kmktime.py index 60e724d..2bda3bb 100644 --- a/kmk/kmktime.py +++ b/kmk/kmktime.py @@ -1,23 +1,28 @@ import time -def sleep_ms(ms: float) -> None: +def sleep_ms(ms): + # type: (float) -> None return time.sleep(ms / 1000) -def ticks_ms() -> float: +def ticks_ms(): + # type: () -> float '''Has .25s granularity, but is cheap''' return time.monotonic() * 1000 -def ticks_diff(new: float, old: float) -> float: +def ticks_diff(new, old): + # type: (float, float) -> float return new - old -def accurate_ticks() -> int: +def accurate_ticks(): + # type: () -> int '''Is more expensive, but good for time critical things''' return time.monotonic_ns() -def accurate_ticks_diff(new: float, old: float, ms: float) -> bool: +def accurate_ticks_diff(new, old, ms): + # type: (float, float, float) -> bool return bool(new - old < ms * 1000000) diff --git a/kmk/types.py b/kmk/types.py index a1c370d..9676497 100644 --- a/kmk/types.py +++ b/kmk/types.py @@ -1,6 +1,12 @@ -from typing import List, Optional, Tuple +import sys -from kmk.keys import Key +from kmk.consts import TYPING_PLATFORMS + +if sys.platform in TYPING_PLATFORMS: + from typing import List, Optional, Tuple, Union + + # Avoid cyclical imports + from kmk.keys import ConsumerKey, Key, ModifierKey class AttrDict(dict): @@ -12,39 +18,44 @@ class AttrDict(dict): This is read-only on purpose. ''' - def __getattr__(self, key: str) -> str: + def __getattr__(self, key): + # type: (str) -> Optional[Union[Key, ModifierKey, ConsumerKey]] return self[key] class LayerKeyMeta: - def __init__(self, layer: int, kc: Optional[Key] = None) -> None: - self.layer: int = layer - self.kc: Optional[Key] = kc + def __init__(self, layer, kc=None): + # type: (int, Optional[Key]) -> None + self.layer = layer # type: int + self.kc = kc # type: Optional[Key] class ModTapKeyMeta: - def __init__( - self, kc: Optional[Key] = None, mods: Optional[List[Key]] = None - ) -> None: - self.mods: Optional[List[Key]] = mods - self.kc: Optional[Key] = kc + def __init__(self, kc=None, mods=None): + # type: (Optional[Key], Optional[List[Key]]) -> None + self.mods = mods # type: Optional[List[Key]] + self.kc = kc # type: Optional[Key] class KeySequenceMeta: - def __init__(self, seq: List[Key]): - self.seq: List[Key] = seq + def __init__(self, seq): + # type: (List[Key]) -> None + self.seq = seq # type: List[Key] class KeySeqSleepMeta: - def __init__(self, ms: float): - self.ms: float = ms + def __init__(self, ms): + # type: (float) -> None + self.ms = ms # type: float class UnicodeModeKeyMeta: - def __init__(self, mode: int): - self.mode: int = mode + def __init__(self, mode): + # type: (int) -> None + self.mode = mode # type: int class TapDanceKeyMeta: - def __init__(self, codes: Tuple[Key, ...]): - self.codes: Tuple[Key, ...] = codes + def __init__(self, codes): + # type: (Tuple[Key, ...]) -> None + self.codes = codes # type: Tuple[Key, ...] diff --git a/pyproject.toml b/pyproject.toml index 314e76d..7ea9f91 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,18 +24,25 @@ exclude = ''' ''' [tool.pyright] +strict = ["kmk"] +typeCheckingMode = "strict" include = ["kmk"] exclude = [ "hardware", ".venv", "user_keymaps", - "boards" + "boards", + ".git" ] +venvPath = ".venv" +# stops constant reporting of missing board module etc. reportMissingModuleSource = false # reports missing typestubs allowing for a code action to # create new library typestubs reportMissingTypeStubs = true +pythonVersion = "3.6" [tool.mypy] exclude = "boards/|user_keymaps/" ignore_missing_imports = true +python_version = "3.6"