From 41a804877512daaf464002b25ed40722aeb0e05b Mon Sep 17 00:00:00 2001 From: sofubi Date: Thu, 12 Aug 2021 12:13:29 -0400 Subject: [PATCH 01/11] Adds mypy and circuitpython-typing to Pipefile Updates Pipfile.lock Updates pyproject.toml with some initial pyright configuration --- Pipfile | 2 + Pipfile.lock | 266 ++++++++++++++++++++++++++++--------------------- pyproject.toml | 9 ++ 3 files changed, 163 insertions(+), 114 deletions(-) diff --git a/Pipfile b/Pipfile index 6003e85..a735877 100644 --- a/Pipfile +++ b/Pipfile @@ -20,3 +20,5 @@ s3cmd = "*" black = "==21.6b0" flake8-quotes = "*" flake8-black = "*" +circuitpython-stubs = "==7.0.0a6.dev195" +mypy = "*" diff --git a/Pipfile.lock b/Pipfile.lock index e18ce53..41d222f 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "0a04ec24d4aef6828e4f5eefa0a7d2c312f21f2b2f18c42c7004cdbe0c02bd53" + "sha256": "e80d827684ac8c56b762a74548440c5b371c5b3ee5fcb5b74e68ae6d9bff0665" }, "pipfile-spec": 6, "requires": {}, @@ -45,6 +45,13 @@ "index": "pypi", "version": "==21.6b0" }, + "circuitpython-stubs": { + "hashes": [ + "sha256:5963ef6b41b03e97049d48142cb23778f3e4ca841620296d7e13b00025522569" + ], + "index": "pypi", + "version": "==7.0.0a6.dev195" + }, "click": { "hashes": [ "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a", @@ -71,11 +78,11 @@ }, "flake8-black": { "hashes": [ - "sha256:941514149cb8b489cb17a4bb1cf18d84375db3b34381bb018de83509437931a0", - "sha256:f26651bc10db786c03f4093414f7c9ea982ed8a244cec323c984feeffdf4c118" + "sha256:c199844bc1b559d91195ebe8620216f21ed67f2cc1ff6884294c91a0d2492684", + "sha256:cc080ba5b3773b69ba102b6617a00cc4ecbad8914109690cfda4d565ea435d96" ], "index": "pypi", - "version": "==0.2.1" + "version": "==0.2.3" }, "flake8-commas": { "hashes": [ @@ -110,58 +117,59 @@ }, "greenlet": { "hashes": [ - "sha256:03f28a5ea20201e70ab70518d151116ce939b412961c33827519ce620957d44c", - "sha256:06d7ac89e6094a0a8f8dc46aa61898e9e1aec79b0f8b47b2400dd51a44dbc832", - "sha256:06ecb43b04480e6bafc45cb1b4b67c785e183ce12c079473359e04a709333b08", - "sha256:096cb0217d1505826ba3d723e8981096f2622cde1eb91af9ed89a17c10aa1f3e", - "sha256:0c557c809eeee215b87e8a7cbfb2d783fb5598a78342c29ade561440abae7d22", - "sha256:0de64d419b1cb1bfd4ea544bedea4b535ef3ae1e150b0f2609da14bbf48a4a5f", - "sha256:14927b15c953f8f2d2a8dffa224aa78d7759ef95284d4c39e1745cf36e8cdd2c", - "sha256:16183fa53bc1a037c38d75fdc59d6208181fa28024a12a7f64bb0884434c91ea", - "sha256:206295d270f702bc27dbdbd7651e8ebe42d319139e0d90217b2074309a200da8", - "sha256:22002259e5b7828b05600a762579fa2f8b33373ad95a0ee57b4d6109d0e589ad", - "sha256:2325123ff3a8ecc10ca76f062445efef13b6cf5a23389e2df3c02a4a527b89bc", - "sha256:258f9612aba0d06785143ee1cbf2d7361801c95489c0bd10c69d163ec5254a16", - "sha256:3096286a6072553b5dbd5efbefc22297e9d06a05ac14ba017233fedaed7584a8", - "sha256:3d13da093d44dee7535b91049e44dd2b5540c2a0e15df168404d3dd2626e0ec5", - "sha256:408071b64e52192869129a205e5b463abda36eff0cebb19d6e63369440e4dc99", - "sha256:598bcfd841e0b1d88e32e6a5ea48348a2c726461b05ff057c1b8692be9443c6e", - "sha256:5d928e2e3c3906e0a29b43dc26d9b3d6e36921eee276786c4e7ad9ff5665c78a", - "sha256:5f75e7f237428755d00e7460239a2482fa7e3970db56c8935bd60da3f0733e56", - "sha256:60848099b76467ef09b62b0f4512e7e6f0a2c977357a036de602b653667f5f4c", - "sha256:6b1d08f2e7f2048d77343279c4d4faa7aef168b3e36039cba1917fffb781a8ed", - "sha256:70bd1bb271e9429e2793902dfd194b653221904a07cbf207c3139e2672d17959", - "sha256:76ed710b4e953fc31c663b079d317c18f40235ba2e3d55f70ff80794f7b57922", - "sha256:7920e3eccd26b7f4c661b746002f5ec5f0928076bd738d38d894bb359ce51927", - "sha256:7db68f15486d412b8e2cfcd584bf3b3a000911d25779d081cbbae76d71bd1a7e", - "sha256:8833e27949ea32d27f7e96930fa29404dd4f2feb13cce483daf52e8842ec246a", - "sha256:944fbdd540712d5377a8795c840a97ff71e7f3221d3fddc98769a15a87b36131", - "sha256:9a6b035aa2c5fcf3dbbf0e3a8a5bc75286fc2d4e6f9cfa738788b433ec894919", - "sha256:9bdcff4b9051fb1aa4bba4fceff6a5f770c6be436408efd99b76fc827f2a9319", - "sha256:a9017ff5fc2522e45562882ff481128631bf35da444775bc2776ac5c61d8bcae", - "sha256:aa4230234d02e6f32f189fd40b59d5a968fe77e80f59c9c933384fe8ba535535", - "sha256:ad80bb338cf9f8129c049837a42a43451fc7c8b57ad56f8e6d32e7697b115505", - "sha256:adb94a28225005890d4cf73648b5131e885c7b4b17bc762779f061844aabcc11", - "sha256:b3090631fecdf7e983d183d0fad7ea72cfb12fa9212461a9b708ff7907ffff47", - "sha256:b33b51ab057f8a20b497ffafdb1e79256db0c03ef4f5e3d52e7497200e11f821", - "sha256:b97c9a144bbeec7039cca44df117efcbeed7209543f5695201cacf05ba3b5857", - "sha256:be13a18cec649ebaab835dff269e914679ef329204704869f2f167b2c163a9da", - "sha256:be9768e56f92d1d7cd94185bab5856f3c5589a50d221c166cc2ad5eb134bd1dc", - "sha256:c1580087ab493c6b43e66f2bdd165d9e3c1e86ef83f6c2c44a29f2869d2c5bd5", - "sha256:c35872b2916ab5a240d52a94314c963476c989814ba9b519bc842e5b61b464bb", - "sha256:c70c7dd733a4c56838d1f1781e769081a25fade879510c5b5f0df76956abfa05", - "sha256:c767458511a59f6f597bfb0032a1c82a52c29ae228c2c0a6865cfeaeaac4c5f5", - "sha256:c87df8ae3f01ffb4483c796fe1b15232ce2b219f0b18126948616224d3f658ee", - "sha256:ca1c4a569232c063615f9e70ff9a1e2fee8c66a6fb5caf0f5e8b21a396deec3e", - "sha256:cc407b68e0a874e7ece60f6639df46309376882152345508be94da608cc0b831", - "sha256:da862b8f7de577bc421323714f63276acb2f759ab8c5e33335509f0b89e06b8f", - "sha256:dfe7eac0d253915116ed0cd160a15a88981a1d194c1ef151e862a5c7d2f853d3", - "sha256:ed1377feed808c9c1139bdb6a61bcbf030c236dd288d6fca71ac26906ab03ba6", - "sha256:f42ad188466d946f1b3afc0a9e1a266ac8926461ee0786c06baac6bd71f8a6f3", - "sha256:f92731609d6625e1cc26ff5757db4d32b6b810d2a3363b0ff94ff573e5901f6f" + "sha256:04e1849c88aa56584d4a0a6e36af5ec7cc37993fdc1fda72b56aa1394a92ded3", + "sha256:05e72db813c28906cdc59bd0da7c325d9b82aa0b0543014059c34c8c4ad20e16", + "sha256:07e6d88242e09b399682b39f8dfa1e7e6eca66b305de1ff74ed9eb1a7d8e539c", + "sha256:090126004c8ab9cd0787e2acf63d79e80ab41a18f57d6448225bbfcba475034f", + "sha256:1796f2c283faab2b71c67e9b9aefb3f201fdfbee5cb55001f5ffce9125f63a45", + "sha256:2f89d74b4f423e756a018832cd7a0a571e0a31b9ca59323b77ce5f15a437629b", + "sha256:34e6675167a238bede724ee60fe0550709e95adaff6a36bcc97006c365290384", + "sha256:3e594015a2349ec6dcceda9aca29da8dc89e85b56825b7d1f138a3f6bb79dd4c", + "sha256:3f8fc59bc5d64fa41f58b0029794f474223693fd00016b29f4e176b3ee2cfd9f", + "sha256:3fc6a447735749d651d8919da49aab03c434a300e9f0af1c886d560405840fd1", + "sha256:40abb7fec4f6294225d2b5464bb6d9552050ded14a7516588d6f010e7e366dcc", + "sha256:44556302c0ab376e37939fd0058e1f0db2e769580d340fb03b01678d1ff25f68", + "sha256:476ba9435afaead4382fbab8f1882f75e3fb2285c35c9285abb3dd30237f9142", + "sha256:4870b018ca685ff573edd56b93f00a122f279640732bb52ce3a62b73ee5c4a92", + "sha256:4adaf53ace289ced90797d92d767d37e7cdc29f13bd3830c3f0a561277a4ae83", + "sha256:4eae94de9924bbb4d24960185363e614b1b62ff797c23dc3c8a7c75bbb8d187e", + "sha256:5317701c7ce167205c0569c10abc4bd01c7f4cf93f642c39f2ce975fa9b78a3c", + "sha256:5c3b735ccf8fc8048664ee415f8af5a3a018cc92010a0d7195395059b4b39b7d", + "sha256:5cde7ee190196cbdc078511f4df0be367af85636b84d8be32230f4871b960687", + "sha256:655ab836324a473d4cd8cf231a2d6f283ed71ed77037679da554e38e606a7117", + "sha256:6ce9d0784c3c79f3e5c5c9c9517bbb6c7e8aa12372a5ea95197b8a99402aa0e6", + "sha256:6e0696525500bc8aa12eae654095d2260db4dc95d5c35af2b486eae1bf914ccd", + "sha256:75ff270fd05125dce3303e9216ccddc541a9e072d4fc764a9276d44dee87242b", + "sha256:8039f5fe8030c43cd1732d9a234fdcbf4916fcc32e21745ca62e75023e4d4649", + "sha256:84488516639c3c5e5c0e52f311fff94ebc45b56788c2a3bfe9cf8e75670f4de3", + "sha256:84782c80a433d87530ae3f4b9ed58d4a57317d9918dfcc6a59115fa2d8731f2c", + "sha256:8ddb38fb6ad96c2ef7468ff73ba5c6876b63b664eebb2c919c224261ae5e8378", + "sha256:98b491976ed656be9445b79bc57ed21decf08a01aaaf5fdabf07c98c108111f6", + "sha256:990e0f5e64bcbc6bdbd03774ecb72496224d13b664aa03afd1f9b171a3269272", + "sha256:9b02e6039eafd75e029d8c58b7b1f3e450ca563ef1fe21c7e3e40b9936c8d03e", + "sha256:a11b6199a0b9dc868990456a2667167d0ba096c5224f6258e452bfbe5a9742c5", + "sha256:a414f8e14aa7bacfe1578f17c11d977e637d25383b6210587c29210af995ef04", + "sha256:a91ee268f059583176c2c8b012a9fce7e49ca6b333a12bbc2dd01fc1a9783885", + "sha256:ac991947ca6533ada4ce7095f0e28fe25d5b2f3266ad5b983ed4201e61596acf", + "sha256:b050dbb96216db273b56f0e5960959c2b4cb679fe1e58a0c3906fa0a60c00662", + "sha256:b97a807437b81f90f85022a9dcfd527deea38368a3979ccb49d93c9198b2c722", + "sha256:bad269e442f1b7ffa3fa8820b3c3aa66f02a9f9455b5ba2db5a6f9eea96f56de", + "sha256:bf3725d79b1ceb19e83fb1aed44095518c0fcff88fba06a76c0891cfd1f36837", + "sha256:c0f22774cd8294078bdf7392ac73cf00bfa1e5e0ed644bd064fdabc5f2a2f481", + "sha256:c1862f9f1031b1dee3ff00f1027fcd098ffc82120f43041fe67804b464bbd8a7", + "sha256:c8d4ed48eed7414ccb2aaaecbc733ed2a84c299714eae3f0f48db085342d5629", + "sha256:cf31e894dabb077a35bbe6963285d4515a387ff657bd25b0530c7168e48f167f", + "sha256:d15cb6f8706678dc47fb4e4f8b339937b04eda48a0af1cca95f180db552e7663", + "sha256:dfcb5a4056e161307d103bc013478892cfd919f1262c2bb8703220adcb986362", + "sha256:e02780da03f84a671bb4205c5968c120f18df081236d7b5462b380fd4f0b497b", + "sha256:e2002a59453858c7f3404690ae80f10c924a39f45f6095f18a985a1234c37334", + "sha256:e22a82d2b416d9227a500c6860cf13e74060cf10e7daf6695cbf4e6a94e0eee4", + "sha256:e41f72f225192d5d4df81dad2974a8943b0f2d664a2a5cfccdf5a01506f5523c", + "sha256:f253dad38605486a4590f9368ecbace95865fea0f2b66615d121ac91fd1a1563", + "sha256:fddfb31aa2ac550b938d952bca8a87f1db0f8dc930ffa14ce05b5c08d27e7fd1" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==1.1.0" + "version": "==1.1.1" }, "ipdb": { "hashes": [ @@ -172,11 +180,11 @@ }, "ipython": { "hashes": [ - "sha256:9bc24a99f5d19721fb8a2d1408908e9c0520a17fff2233ffe82620847f17f1b6", - "sha256:d513e93327cf8657d6467c81f1f894adc125334ffe0e4ddd1abbb1c78d828703" + "sha256:0cff04bb042800129348701f7bd68a430a844e8fb193979c08f6c99f28bb735e", + "sha256:892743b65c21ed72b806a3a602cca408520b3200b89d1924f4b3d2cdb3692362" ], "index": "pypi", - "version": "==7.24.1" + "version": "==7.26.0" }, "ipython-genutils": { "hashes": [ @@ -187,11 +195,11 @@ }, "isort": { "hashes": [ - "sha256:0a943902919f65c5684ac4e0154b1ad4fac6dcaa5d9f3426b732f1c8b5419be6", - "sha256:2bb1680aad211e3c9944dbce1d4ba09a989f04e238296c87fe2139faa26d655d" + "sha256:9c2ea1e62d871267b78307fe511c0838ba0da28698c5732d54e2790bf3ba9899", + "sha256:e17d6e2b81095c9db0a03a8025a957f334d6ea30b26f9ec70805411e5c7c81f2" ], "index": "pypi", - "version": "==5.8.0" + "version": "==5.9.3" }, "jedi": { "hashes": [ @@ -249,6 +257,35 @@ ], "version": "==1.0.2" }, + "mypy": { + "hashes": [ + "sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9", + "sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a", + "sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9", + "sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e", + "sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2", + "sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212", + "sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b", + "sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885", + "sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150", + "sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703", + "sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072", + "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457", + "sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e", + "sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0", + "sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb", + "sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97", + "sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8", + "sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811", + "sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6", + "sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de", + "sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504", + "sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921", + "sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d" + ], + "index": "pypi", + "version": "==0.910" + }, "mypy-extensions": { "hashes": [ "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", @@ -273,10 +310,10 @@ }, "pathspec": { "hashes": [ - "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd", - "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d" + "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a", + "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1" ], - "version": "==0.8.1" + "version": "==0.9.0" }, "pexpect": { "hashes": [ @@ -347,18 +384,19 @@ }, "python-dateutil": { "hashes": [ - "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", - "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a" + "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", + "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.1" + "version": "==2.8.2" }, "python-dotenv": { "hashes": [ - "sha256:dd8fe852847f4fbfadabf6183ddd4c824a9651f02d51714fa075c95561959c7d", - "sha256:effaac3c1e58d89b3ccb4d04a40dc7ad6e0275fda25fd75ae9d323e2465e202d" + "sha256:aae25dc1ebe97c420f50b81fb0e5c949659af713f31fdb63c749ca68748f34b1", + "sha256:f521bc2ac9a8e03c736f62911605c5d83970021e3fa95b37d769e2bbbe9b6172" ], - "version": "==0.18.0" + "markers": "python_version >= '3.5'", + "version": "==0.19.0" }, "python-magic": { "hashes": [ @@ -370,49 +408,41 @@ }, "regex": { "hashes": [ - "sha256:01afaf2ec48e196ba91b37451aa353cb7eda77efe518e481707e0515025f0cd5", - "sha256:11d773d75fa650cd36f68d7ca936e3c7afaae41b863b8c387a22aaa78d3c5c79", - "sha256:18c071c3eb09c30a264879f0d310d37fe5d3a3111662438889ae2eb6fc570c31", - "sha256:1e1c20e29358165242928c2de1482fb2cf4ea54a6a6dea2bd7a0e0d8ee321500", - "sha256:281d2fd05555079448537fe108d79eb031b403dac622621c78944c235f3fcf11", - "sha256:314d66636c494ed9c148a42731b3834496cc9a2c4251b1661e40936814542b14", - "sha256:32e65442138b7b76dd8173ffa2cf67356b7bc1768851dded39a7a13bf9223da3", - "sha256:339456e7d8c06dd36a22e451d58ef72cef293112b559010db3d054d5560ef439", - "sha256:3916d08be28a1149fb97f7728fca1f7c15d309a9f9682d89d79db75d5e52091c", - "sha256:3a9cd17e6e5c7eb328517969e0cb0c3d31fd329298dd0c04af99ebf42e904f82", - "sha256:47bf5bf60cf04d72bf6055ae5927a0bd9016096bf3d742fa50d9bf9f45aa0711", - "sha256:4c46e22a0933dd783467cf32b3516299fb98cfebd895817d685130cc50cd1093", - "sha256:4c557a7b470908b1712fe27fb1ef20772b78079808c87d20a90d051660b1d69a", - "sha256:52ba3d3f9b942c49d7e4bc105bb28551c44065f139a65062ab7912bef10c9afb", - "sha256:563085e55b0d4fb8f746f6a335893bda5c2cef43b2f0258fe1020ab1dd874df8", - "sha256:598585c9f0af8374c28edd609eb291b5726d7cbce16be6a8b95aa074d252ee17", - "sha256:619d71c59a78b84d7f18891fe914446d07edd48dc8328c8e149cbe0929b4e000", - "sha256:67bdb9702427ceddc6ef3dc382455e90f785af4c13d495f9626861763ee13f9d", - "sha256:6d1b01031dedf2503631d0903cb563743f397ccaf6607a5e3b19a3d76fc10480", - "sha256:741a9647fcf2e45f3a1cf0e24f5e17febf3efe8d4ba1281dcc3aa0459ef424dc", - "sha256:7c2a1af393fcc09e898beba5dd59196edaa3116191cc7257f9224beaed3e1aa0", - "sha256:7d9884d86dd4dd489e981d94a65cd30d6f07203d90e98f6f657f05170f6324c9", - "sha256:90f11ff637fe8798933fb29f5ae1148c978cccb0452005bf4c69e13db951e765", - "sha256:919859aa909429fb5aa9cf8807f6045592c85ef56fdd30a9a3747e513db2536e", - "sha256:96fcd1888ab4d03adfc9303a7b3c0bd78c5412b2bfbe76db5b56d9eae004907a", - "sha256:97f29f57d5b84e73fbaf99ab3e26134e6687348e95ef6b48cfd2c06807005a07", - "sha256:980d7be47c84979d9136328d882f67ec5e50008681d94ecc8afa8a65ed1f4a6f", - "sha256:a91aa8619b23b79bcbeb37abe286f2f408d2f2d6f29a17237afda55bb54e7aac", - "sha256:ade17eb5d643b7fead300a1641e9f45401c98eee23763e9ed66a43f92f20b4a7", - "sha256:b9c3db21af35e3b3c05764461b262d6f05bbca08a71a7849fd79d47ba7bc33ed", - "sha256:bd28bc2e3a772acbb07787c6308e00d9626ff89e3bfcdebe87fa5afbfdedf968", - "sha256:bf5824bfac591ddb2c1f0a5f4ab72da28994548c708d2191e3b87dd207eb3ad7", - "sha256:c0502c0fadef0d23b128605d69b58edb2c681c25d44574fc673b0e52dce71ee2", - "sha256:c38c71df845e2aabb7fb0b920d11a1b5ac8526005e533a8920aea97efb8ec6a4", - "sha256:ce15b6d103daff8e9fee13cf7f0add05245a05d866e73926c358e871221eae87", - "sha256:d3029c340cfbb3ac0a71798100ccc13b97dddf373a4ae56b6a72cf70dfd53bc8", - "sha256:e512d8ef5ad7b898cdb2d8ee1cb09a8339e4f8be706d27eaa180c2f177248a10", - "sha256:e8e5b509d5c2ff12f8418006d5a90e9436766133b564db0abaec92fd27fcee29", - "sha256:ee54ff27bf0afaf4c3b3a62bcd016c12c3fdb4ec4f413391a90bd38bc3624605", - "sha256:fa4537fb4a98fe8fde99626e4681cc644bdcf2a795038533f9f711513a862ae6", - "sha256:fd45ff9293d9274c5008a2054ecef86a9bfe819a67c7be1afb65e69b405b3042" + "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" ], - "version": "==2021.4.4" + "version": "==2021.8.3" }, "s3cmd": { "hashes": [ @@ -432,10 +462,10 @@ }, "testfixtures": { "hashes": [ - "sha256:5ec3a0dd6f71cc4c304fbc024a10cc293d3e0b852c868014b9f233203e149bda", - "sha256:9ed31e83f59619e2fa17df053b241e16e0608f4580f7b5a9333a0c9bdcc99137" + "sha256:9bddf79b2dddb36420a20c25a65c827a8e7398c6ed4e2c75c2697857cb006be9", + "sha256:d4bd1c4f90eac90a73e1bdc59c31d03943f218d687f3c5a09e48478841a8af5f" ], - "version": "==6.17.1" + "version": "==6.18.0" }, "toml": { "hashes": [ @@ -453,6 +483,14 @@ "markers": "python_version >= '3.7'", "version": "==5.0.5" }, + "typing-extensions": { + "hashes": [ + "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497", + "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342", + "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84" + ], + "version": "==3.10.0.0" + }, "wcwidth": { "hashes": [ "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784", diff --git a/pyproject.toml b/pyproject.toml index 859393f..08597ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,3 +22,12 @@ exclude = ''' | mnt )/ ''' + +[tool.pyright] +include = ["kmk"] +exclude = [ + "hardware", + ".venv", + "user_keymaps" +] +reportMissingModuleSource = false From 317d14fdac101cca67d71410c3daaea1867ea810 Mon Sep 17 00:00:00 2001 From: sofubi Date: Thu, 12 Aug 2021 12:15:38 -0400 Subject: [PATCH 02/11] Add typing to encoder.py --- kmk/modules/encoder.py | 141 ++++++++++++++++++++++++----------------- 1 file changed, 83 insertions(+), 58 deletions(-) diff --git a/kmk/modules/encoder.py b/kmk/modules/encoder.py index 86c841f..1d9341b 100644 --- a/kmk/modules/encoder.py +++ b/kmk/modules/encoder.py @@ -1,66 +1,89 @@ import digitalio +from microcontroller import Pin +from typing import Any, Dict, List, Optional, Tuple, Union + +from kmk.keys import KeyAttrDict from kmk.kmktime import ticks_ms +from kmk.kmk_keyboard import KMKKeyboard from kmk.modules import Module +EncoderMap = Tuple[ + List[Tuple[KeyAttrDict, KeyAttrDict, int]], + List[Tuple[KeyAttrDict, KeyAttrDict, int]], + List[Tuple[None, None, int]], +] + class EncoderPadState: - OFF = False - ON = True + OFF: bool = False + ON: bool = True class EndcoderDirection: - Left = False - Right = True + Left: bool = False + Right: bool = True class Encoder: def __init__( self, - pad_a, - pad_b, - button_pin=None, - ): - self.pad_a = self.PreparePin(pad_a) # board pin for enc pin a - self.pad_a_state = False - self.pad_b = self.PreparePin(pad_b) # board pin for enc pin b - self.pad_b_state = False - self.button_pin = self.PreparePin(button_pin) # board pin for enc btn + pad_a: Pin, + pad_b: Pin, + button_pin: Optional[Pin] = None, + ) -> None: + self.pad_a: Union[digitalio.DigitalInOut, None] = self.PreparePin( + pad_a + ) # board pin for enc pin a + self.pad_a_state: bool = False + self.pad_b: Union[digitalio.DigitalInOut, None] = self.PreparePin( + pad_b + ) # board pin for enc pin b + self.pad_b_state: bool = False + self.button_pin: Union[digitalio.DigitalInOut, None] = self.PreparePin( + button_pin + ) # board pin for enc btn self.button_state = None # state of pushbutton on encoder if enabled - self.encoder_value = 0 # clarify what this value is - self.encoder_state = ( + self.encoder_value: int = 0 # clarify what this value is + self.encoder_state: Tuple[bool, bool] = ( self.pad_a_state, self.pad_b_state, ) # quaderature encoder state - self.encoder_direction = None # arbitrary, tells direction of knob - self.last_encoder_state = None # not used yet - self.resolution = 2 # number of keys sent per position change - self.revolution_count = 20 # position changes per revolution - self.has_button = False # enable/disable button functionality - self.encoder_data = None # 6tuple containing all encoder data - self.position_change = None # revolution count, inc/dec as knob turns - self.last_encoder_value = 0 # not used - self.is_inverted = False # switch to invert knob direction - self.vel_mode = False # enable the velocity output - self.vel_ts = None # velocity timestamp - self.last_vel_ts = 0 # last velocity timestamp - self.encoder_speed = None # ms per position change(4 states) - self.encoder_map = None - self.eps = EncoderPadState() - self.encoder_pad_lookup = { + self.encoder_direction: Optional[ + bool + ] = None # arbitrary, tells direction of knob + self.last_encoder_state: Optional[Tuple[bool, bool]] = None # not used yet + self.resolution: int = 2 # number of keys sent per position change + self.revolution_count: int = 20 # position changes per revolution + self.has_button: bool = False # enable/disable button functionality + self.encoder_data: Optional[Tuple] = None # 6tuple containing all encoder data + self.position_change: Optional[ + int + ] = None # revolution count, inc/dec as knob turns + self.last_encoder_value: int = 0 # not used + self.is_inverted: bool = False # switch to invert knob direction + self.vel_mode: bool = False # enable the velocity output + self.vel_ts: Optional[float] = None # velocity timestamp + self.last_vel_ts: float = 0 # last velocity timestamp + self.encoder_speed: Optional[float] = None # ms per position change(4 states) + self.encoder_map: Optional[EncoderMap] = None + self.eps: EncoderPadState = EncoderPadState() + self.encoder_pad_lookup: Dict[bool, bool] = { False: self.eps.OFF, True: self.eps.ON, } - self.edr = EndcoderDirection() # lookup for current encoder direction - self.encoder_dir_lookup = { + self.edr: EndcoderDirection = ( + EndcoderDirection() + ) # lookup for current encoder direction + self.encoder_dir_lookup: Dict[bool, bool] = { False: self.edr.Left, True: self.edr.Right, } - def __repr__(self, idx): + def __repr__(self, idx: int) -> str: return 'ENCODER_{}({})'.format(idx, self._to_dict()) - def _to_dict(self): + def _to_dict(self) -> Dict[str, Any]: return { 'Encoder_State': self.encoder_state, 'Direction': self.encoder_direction, @@ -71,7 +94,7 @@ class Encoder: } # adapted for CircuitPython from raspi - def PreparePin(self, num): + def PreparePin(self, num: Union[Pin, None]) -> Union[digitalio.DigitalInOut, None]: if num is not None: pad = digitalio.DigitalInOut(num) pad.direction = digitalio.Direction.INPUT @@ -81,7 +104,7 @@ class Encoder: return None # checks encoder pins, reports encoder data - def report(self): + def report(self) -> Union[int, None]: new_encoder_state = ( self.encoder_pad_lookup[int(self.pad_a.value)], self.encoder_pad_lookup[int(self.pad_b.value)], @@ -143,14 +166,14 @@ class Encoder: return None # invert knob direction if encoder pins are soldered backwards - def invert_rotation(self, new, old): + def invert_rotation(self, new: int, old: int) -> int: if self.is_inverted: return -(new - old) else: return new - old # returns knob velocity as milliseconds between position changes(detents) - def vel_report(self): + def vel_report(self) -> float: self.encoder_speed = self.vel_ts - self.last_vel_ts self.last_vel_ts = self.vel_ts return self.encoder_speed @@ -158,50 +181,52 @@ class Encoder: class EncoderHandler(Module): - encoders = [] - debug_enabled = False # not working as inttended, do not use for now + encoders: List[Encoder] = [] + debug_enabled: bool = False # not working as inttended, do not use for now - def __init__(self, pad_a, pad_b, encoder_map): - self.pad_a = pad_a - self.pad_b = pad_b - self.encoder_count = len(self.pad_a) - self.encoder_map = encoder_map + def __init__( + self, pad_a: List[Pin], pad_b: List[Pin], encoder_map: EncoderMap + ) -> None: + self.pad_a: List[Pin] = pad_a + self.pad_b: List[Pin] = pad_b + self.encoder_count: int = len(self.pad_a) + self.encoder_map: EncoderMap = encoder_map self.make_encoders() - def on_runtime_enable(self, keyboard): + def on_runtime_enable(self, keyboard: KMKKeyboard) -> None: return - def on_runtime_disable(self, keyboard): + def on_runtime_disable(self, keyboard: KMKKeyboard) -> None: return - def during_bootup(self, keyboard): + def during_bootup(self, keyboard: KMKKeyboard) -> None: return - def before_matrix_scan(self, keyboard): + def before_matrix_scan(self, keyboard: KMKKeyboard) -> Union[KMKKeyboard, None]: ''' Return value will be injected as an extra matrix update ''' return self.get_reports(keyboard) - def after_matrix_scan(self, keyboard): + def after_matrix_scan(self, keyboard: KMKKeyboard) -> None: ''' Return value will be replace matrix update if supplied ''' return - def before_hid_send(self, keyboard): + def before_hid_send(self, keyboard: KMKKeyboard) -> None: return - def after_hid_send(self, keyboard): + def after_hid_send(self, keyboard: KMKKeyboard) -> None: return - def on_powersave_enable(self, keyboard): + def on_powersave_enable(self, keyboard: KMKKeyboard) -> None: return - def on_powersave_disable(self, keyboard): + def on_powersave_disable(self, keyboard: KMKKeyboard) -> None: return - def make_encoders(self): + def make_encoders(self) -> None: for i in range(self.encoder_count): self.encoders.append( Encoder( @@ -210,7 +235,7 @@ class EncoderHandler(Module): ) ) - def send_encoder_keys(self, keyboard, encoder_key, encoder_idx): + def send_encoder_keys(self, keyboard: KMKKeyboard, encoder_key: int, encoder_idx: int) -> KMKKeyboard: # position in the encoder map tuple encoder_resolution = 2 for _ in range( @@ -221,7 +246,7 @@ class EncoderHandler(Module): ) return keyboard - def get_reports(self, keyboard): + def get_reports(self, keyboard: KMKKeyboard) -> Union[KMKKeyboard, None]: for idx in range(self.encoder_count): if self.debug_enabled: # not working as inttended, do not use for now print(self.encoders[idx].__repr__(idx)) From 95dcc57e76e489b2a2ed899e7e2aab145ac9c9ca Mon Sep 17 00:00:00 2001 From: sofubi Date: Fri, 13 Aug 2021 16:00:57 -0400 Subject: [PATCH 03/11] Finish type annotations for modules folder Update pyproject.toml --- kmk/modules/__init__.py | 19 +++--- kmk/modules/encoder.py | 27 ++++----- kmk/modules/layers.py | 57 +++++++++--------- kmk/modules/modtap.py | 27 +++++---- kmk/modules/power.py | 59 ++++++++++--------- kmk/modules/split.py | 124 +++++++++++++++++++++------------------- pyproject.toml | 1 + 7 files changed, 168 insertions(+), 146 deletions(-) diff --git a/kmk/modules/__init__.py b/kmk/modules/__init__.py index e78b6c9..9bb95cf 100644 --- a/kmk/modules/__init__.py +++ b/kmk/modules/__init__.py @@ -1,3 +1,8 @@ +from typing import NoReturn + +from kmk.kmk_keyboard import KMKKeyboard + + class InvalidExtensionEnvironment(Exception): pass @@ -12,29 +17,29 @@ class Module: # The below methods should be implemented by subclasses - def during_bootup(self, keyboard): + def during_bootup(self, keyboard: KMKKeyboard) -> NoReturn: raise NotImplementedError - def before_matrix_scan(self, keyboard): + def before_matrix_scan(self, keyboard: KMKKeyboard) -> NoReturn: ''' Return value will be injected as an extra matrix update ''' raise NotImplementedError - def after_matrix_scan(self, keyboard): + def after_matrix_scan(self, keyboard: KMKKeyboard) -> NoReturn: ''' Return value will be replace matrix update if supplied ''' raise NotImplementedError - def before_hid_send(self, keyboard): + def before_hid_send(self, keyboard: KMKKeyboard) -> NoReturn: raise NotImplementedError - def after_hid_send(self, keyboard): + def after_hid_send(self, keyboard: KMKKeyboard) -> NoReturn: raise NotImplementedError - def on_powersave_enable(self, keyboard): + def on_powersave_enable(self, keyboard: KMKKeyboard) -> NoReturn: raise NotImplementedError - def on_powersave_disable(self, keyboard): + def on_powersave_disable(self, keyboard: KMKKeyboard) -> NoReturn: raise NotImplementedError diff --git a/kmk/modules/encoder.py b/kmk/modules/encoder.py index 1d9341b..e5f0d30 100644 --- a/kmk/modules/encoder.py +++ b/kmk/modules/encoder.py @@ -1,11 +1,10 @@ import digitalio -from microcontroller import Pin -from typing import Any, Dict, List, Optional, Tuple, Union +from typing import Any, ClassVar, Dict, List, Optional, Tuple, Union from kmk.keys import KeyAttrDict -from kmk.kmktime import ticks_ms from kmk.kmk_keyboard import KMKKeyboard +from kmk.kmktime import ticks_ms from kmk.modules import Module EncoderMap = Tuple[ @@ -28,9 +27,9 @@ class EndcoderDirection: class Encoder: def __init__( self, - pad_a: Pin, - pad_b: Pin, - button_pin: Optional[Pin] = None, + pad_a: Any, + pad_b: Any, + button_pin: Optional[Any] = None, ) -> None: self.pad_a: Union[digitalio.DigitalInOut, None] = self.PreparePin( pad_a @@ -94,7 +93,7 @@ class Encoder: } # adapted for CircuitPython from raspi - def PreparePin(self, num: Union[Pin, None]) -> Union[digitalio.DigitalInOut, None]: + def PreparePin(self, num: Union[Any, None]) -> Union[digitalio.DigitalInOut, None]: if num is not None: pad = digitalio.DigitalInOut(num) pad.direction = digitalio.Direction.INPUT @@ -181,14 +180,14 @@ class Encoder: class EncoderHandler(Module): - encoders: List[Encoder] = [] - debug_enabled: bool = False # not working as inttended, do not use for now + encoders: ClassVar[List[Encoder]] = [] + debug_enabled: ClassVar[bool] = False # not working as inttended, do not use for now def __init__( - self, pad_a: List[Pin], pad_b: List[Pin], encoder_map: EncoderMap + self, pad_a: List[Any], pad_b: List[Any], encoder_map: EncoderMap ) -> None: - self.pad_a: List[Pin] = pad_a - self.pad_b: List[Pin] = pad_b + self.pad_a: List[Any] = pad_a + self.pad_b: List[Any] = pad_b self.encoder_count: int = len(self.pad_a) self.encoder_map: EncoderMap = encoder_map self.make_encoders() @@ -235,7 +234,9 @@ class EncoderHandler(Module): ) ) - def send_encoder_keys(self, keyboard: KMKKeyboard, encoder_key: int, encoder_idx: int) -> KMKKeyboard: + def send_encoder_keys( + self, keyboard: KMKKeyboard, encoder_key: int, encoder_idx: int + ) -> KMKKeyboard: # position in the encoder map tuple encoder_resolution = 2 for _ in range( diff --git a/kmk/modules/layers.py b/kmk/modules/layers.py index 416bc27..477db07 100644 --- a/kmk/modules/layers.py +++ b/kmk/modules/layers.py @@ -1,8 +1,11 @@ '''One layer isn't enough. Adds keys to get to more of them''' from micropython import const +from typing import Any, Dict, Optional + from kmk.key_validators import layer_key_validator -from kmk.keys import make_argumented_key +from kmk.keys import Key, make_argumented_key +from kmk.kmk_keyboard import KMKKeyboard from kmk.kmktime import accurate_ticks, accurate_ticks_diff from kmk.modules import Module @@ -10,20 +13,20 @@ from kmk.modules import Module class LayerType: '''Defines layer type values for readability''' - MO = const(0) - DF = const(1) - LM = const(2) - LT = const(3) - TG = const(4) - TT = const(5) + MO: int = const(0) + DF: int = const(1) + LM: int = const(2) + LT: int = const(3) + TG: int = const(4) + TT: int = const(5) class Layers(Module): '''Gives access to the keys used to enable the layer system''' - def __init__(self): + def __init__(self) -> None: # Layers - self.start_time = { + self.start_time: Dict[int, Optional[int]] = { LayerType.LT: None, LayerType.TG: None, LayerType.TT: None, @@ -63,41 +66,41 @@ class Layers(Module): on_release=self._tt_released, ) - def during_bootup(self, keyboard): + def during_bootup(self, keyboard: KMKKeyboard) -> None: return - def before_matrix_scan(self, keyboard): + def before_matrix_scan(self, keyboard: KMKKeyboard) -> None: return - def after_matrix_scan(self, keyboard): + def after_matrix_scan(self, keyboard: KMKKeyboard) -> None: return - def before_hid_send(self, keyboard): + def before_hid_send(self, keyboard: KMKKeyboard) -> None: return - def after_hid_send(self, keyboard): + def after_hid_send(self, keyboard: KMKKeyboard) -> None: return - def on_powersave_enable(self, keyboard): + def on_powersave_enable(self, keyboard: KMKKeyboard) -> None: return - def on_powersave_disable(self, keyboard): + def on_powersave_disable(self, keyboard: KMKKeyboard) -> None: return - def _df_pressed(self, key, keyboard, *args, **kwargs): + def _df_pressed(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> None: ''' Switches the default layer ''' keyboard.active_layers[-1] = key.meta.layer - def _mo_pressed(self, key, keyboard, *args, **kwargs): + def _mo_pressed(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> None: ''' Momentarily activates layer, switches off when you let go ''' keyboard.active_layers.insert(0, key.meta.layer) @staticmethod - def _mo_released(key, keyboard, *args, **kwargs): + def _mo_released(key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> None: # remove the first instance of the target layer # from the active list # under almost all normal use cases, this will @@ -112,7 +115,7 @@ class Layers(Module): except ValueError: pass - def _lm_pressed(self, key, keyboard, *args, **kwargs): + def _lm_pressed(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> None: ''' As MO(layer) but with mod active ''' @@ -121,7 +124,7 @@ class Layers(Module): keyboard.keys_pressed.add(key.meta.kc) self._mo_pressed(key, keyboard, *args, **kwargs) - def _lm_released(self, key, keyboard, *args, **kwargs): + def _lm_released(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> None: ''' As MO(layer) but with mod active ''' @@ -129,12 +132,12 @@ class Layers(Module): keyboard.keys_pressed.discard(key.meta.kc) self._mo_released(key, keyboard, *args, **kwargs) - def _lt_pressed(self, key, keyboard, *args, **kwargs): + def _lt_pressed(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> None: # Sets the timer start and acts like MO otherwise self.start_time[LayerType.LT] = accurate_ticks() self._mo_pressed(key, keyboard, *args, **kwargs) - def _lt_released(self, key, keyboard, *args, **kwargs): + def _lt_released(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> None: # On keyup, check timer, and press key if needed. if self.start_time[LayerType.LT] and ( accurate_ticks_diff( @@ -147,7 +150,7 @@ class Layers(Module): self._mo_released(key, keyboard, *args, **kwargs) self.start_time[LayerType.LT] = None - def _tg_pressed(self, key, keyboard, *args, **kwargs): + def _tg_pressed(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> None: ''' Toggles the layer (enables it if not active, and vise versa) ''' @@ -158,14 +161,14 @@ class Layers(Module): except ValueError: keyboard.active_layers.insert(0, key.meta.layer) - def _to_pressed(self, key, keyboard, *args, **kwargs): + def _to_pressed(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> None: ''' Activates layer and deactivates all other layers ''' keyboard.active_layers.clear() keyboard.active_layers.insert(0, key.meta.layer) - def _tt_pressed(self, key, keyboard, *args, **kwargs): + def _tt_pressed(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> None: ''' Momentarily activates layer if held, toggles it if tapped repeatedly ''' @@ -181,7 +184,7 @@ class Layers(Module): return return - def _tt_released(self, key, keyboard, *args, **kwargs): + def _tt_released(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> None: if self.start_time[LayerType.TT] is None or not accurate_ticks_diff( accurate_ticks(), self.start_time[LayerType.TT], keyboard.tap_time ): diff --git a/kmk/modules/modtap.py b/kmk/modules/modtap.py index 5402899..b92baeb 100644 --- a/kmk/modules/modtap.py +++ b/kmk/modules/modtap.py @@ -1,12 +1,15 @@ +from typing import Any, Optional + from kmk.key_validators import mod_tap_validator -from kmk.keys import make_argumented_key +from kmk.keys import Key, make_argumented_key +from kmk.kmk_keyboard import KMKKeyboard from kmk.kmktime import accurate_ticks, accurate_ticks_diff from kmk.modules import Module class ModTap(Module): - def __init__(self): - self._mod_tap_timer = None + def __init__(self) -> None: + self._mod_tap_timer: Optional[int] = None make_argumented_key( validator=mod_tap_validator, names=('MT',), @@ -14,35 +17,35 @@ class ModTap(Module): on_release=self.mt_released, ) - def during_bootup(self, keyboard): + def during_bootup(self, keyboard: KMKKeyboard) -> None: return - def before_matrix_scan(self, keyboard): + def before_matrix_scan(self, keyboard: KMKKeyboard) -> None: return - def after_matrix_scan(self, keyboard): + def after_matrix_scan(self, keyboard: KMKKeyboard) -> None: return - def before_hid_send(self, keyboard): + def before_hid_send(self, keyboard: KMKKeyboard) -> None: return - def after_hid_send(self, keyboard): + def after_hid_send(self, keyboard: KMKKeyboard) -> None: return - def on_powersave_enable(self, keyboard): + def on_powersave_enable(self, keyboard: KMKKeyboard) -> None: return - def on_powersave_disable(self, keyboard): + def on_powersave_disable(self, keyboard: KMKKeyboard) -> None: return - def mt_pressed(self, key, keyboard, *args, **kwargs): + def mt_pressed(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> KMKKeyboard: '''Sets the timer start and acts like a modifier otherwise''' keyboard.keys_pressed.add(key.meta.mods) self._mod_tap_timer = accurate_ticks() return keyboard - def mt_released(self, key, keyboard, *args, **kwargs): + def mt_released(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> KMKKeyboard: '''On keyup, check timer, and press key if needed.''' keyboard.keys_pressed.discard(key.meta.mods) if self._mod_tap_timer and ( diff --git a/kmk/modules/power.py b/kmk/modules/power.py index b0a09d6..9b4703e 100644 --- a/kmk/modules/power.py +++ b/kmk/modules/power.py @@ -1,21 +1,24 @@ +from kmk.kmk_keyboard import KMKKeyboard import board import digitalio +from typing import Any, Dict, Optional + from kmk.handlers.stock import passthrough as handler_passthrough -from kmk.keys import make_key +from kmk.keys import Key, make_key from kmk.kmktime import sleep_ms, ticks_diff, ticks_ms from kmk.modules import Module class Power(Module): - def __init__(self, powersave_pin=None): - self.enable = False + def __init__(self, powersave_pin: Optional[Any] = None) -> None: + self.enable: bool = False self.powersave_pin = powersave_pin # Powersave pin board object - self._powersave_start = ticks_ms() - self._usb_last_scan = ticks_ms() - 5000 - self._psp = None # Powersave pin object - self._i2c = 0 - self._loopcounter = 0 + self._powersave_start: float = ticks_ms() + self._usb_last_scan: float = ticks_ms() - 5000 + self._psp: Optional[digitalio.DigitalInOut] = None # Powersave pin object + self._i2c: int = 0 + self._loopcounter: int = 0 make_key( names=('PS_TOG',), on_press=self._ps_tog, on_release=handler_passthrough @@ -27,10 +30,10 @@ class Power(Module): names=('PS_OFF',), on_press=self._ps_disable, on_release=handler_passthrough ) - def __repr__(self): + def __repr__(self) -> str: return f'Power({self._to_dict()})' - def _to_dict(self): + def _to_dict(self) -> Dict[str, Any]: return { 'enable': self.enable, 'powersave_pin': self.powersave_pin, @@ -39,24 +42,24 @@ class Power(Module): '_psp': self._psp, } - def during_bootup(self, keyboard): + def during_bootup(self, keyboard: KMKKeyboard) -> None: self._i2c_scan() - def before_matrix_scan(self, keyboard): + def before_matrix_scan(self, keyboard: KMKKeyboard) -> None: return - def after_matrix_scan(self, keyboard): + def after_matrix_scan(self, keyboard: KMKKeyboard) -> None: if keyboard.matrix_update or keyboard.secondary_matrix_update: self.psave_time_reset() - def before_hid_send(self, keyboard): + def before_hid_send(self, keyboard: KMKKeyboard) -> None: return - def after_hid_send(self, keyboard): + def after_hid_send(self, keyboard: KMKKeyboard) -> None: if self.enable: self.psleep() - def on_powersave_enable(self, keyboard): + def on_powersave_enable(self, keyboard: KMKKeyboard) -> None: '''Gives 10 cycles to allow other extensions to clean up before powersave''' if self._loopcounter > 10: self.enable_powersave(keyboard) @@ -65,11 +68,11 @@ class Power(Module): self._loopcounter += 1 return - def on_powersave_disable(self, keyboard): + def on_powersave_disable(self, keyboard: KMKKeyboard) -> None: self.disable_powersave(keyboard) return - def enable_powersave(self, keyboard): + def enable_powersave(self, keyboard: KMKKeyboard) -> None: '''Enables power saving features''' if keyboard.i2c_deinit_count >= self._i2c and self.powersave_pin: # Allows power save to prevent RGB drain. @@ -85,7 +88,7 @@ class Power(Module): keyboard._trigger_powersave_enable = False return - def disable_powersave(self, keyboard): + def disable_powersave(self, keyboard: KMKKeyboard) -> None: '''Disables power saving features''' if self._psp: self._psp.value = False @@ -96,7 +99,7 @@ class Power(Module): self.enable = False return - def psleep(self): + def psleep(self) -> None: ''' Sleeps longer and longer to save power the more time in between updates. ''' @@ -106,10 +109,10 @@ class Power(Module): sleep_ms(180) return - def psave_time_reset(self): + def psave_time_reset(self) -> None: self._powersave_start = ticks_ms() - def _i2c_scan(self): + def _i2c_scan(self) -> None: i2c = board.I2C() while not i2c.try_lock(): pass @@ -119,28 +122,28 @@ class Power(Module): i2c.unlock() return - def usb_rescan_timer(self): + def usb_rescan_timer(self) -> bool: return bool(ticks_diff(ticks_ms(), self._usb_last_scan) > 5000) - def usb_time_reset(self): + def usb_time_reset(self) -> None: self._usb_last_scan = ticks_ms() return - def usb_scan(self): + def usb_scan(self) -> bool: # TODO Add USB detection here. Currently lies that it's connected # https://github.com/adafruit/circuitpython/pull/3513 return True - def _ps_tog(self, key, keyboard, *args, **kwargs): + def _ps_tog(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> None: if self.enable: keyboard._trigger_powersave_disable = True else: keyboard._trigger_powersave_enable = True - def _ps_enable(self, key, keyboard, *args, **kwargs): + def _ps_enable(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> None: if not self.enable: keyboard._trigger_powersave_enable = True - def _ps_disable(self, key, keyboard, *args, **kwargs): + def _ps_disable(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> None: if self.enable: keyboard._trigger_powersave_disable = True diff --git a/kmk/modules/split.py b/kmk/modules/split.py index 0de7e17..6d30e7d 100644 --- a/kmk/modules/split.py +++ b/kmk/modules/split.py @@ -3,22 +3,26 @@ import busio from micropython import const from storage import getmount +from typing import Any, List, Optional, Tuple, Type, Union +from kmk.kmk_keyboard import KMKKeyboard from kmk.kmktime import ticks_diff, ticks_ms from kmk.matrix import intify_coordinate from kmk.modules import Module +UartBuffer = List[Optional[Union[bytes, None]]] + class SplitSide: - LEFT = const(1) - RIGHT = const(2) + LEFT: int = const(1) + RIGHT: int = const(2) class SplitType: - UART = const(1) - I2C = const(2) # unused - ONEWIRE = const(3) # unused - BLE = const(4) + UART: int = const(1) + I2C: int = const(2) # unused + ONEWIRE: int = const(3) # unused + BLE: int = const(4) class Split(Module): @@ -26,56 +30,58 @@ class Split(Module): def __init__( self, - split_flip=True, - split_side=None, - split_type=SplitType.UART, - split_target_left=True, - uart_interval=20, - data_pin=None, - data_pin2=None, - target_left=True, - uart_flip=True, - debug_enabled=False, - ): - self._is_target = True - self._uart_buffer = [] - self.split_flip = split_flip - self.split_side = split_side - self.split_type = split_type - self.split_target_left = split_target_left - self.split_offset = None - self.data_pin = data_pin - self.data_pin2 = data_pin2 - self.target_left = target_left - self.uart_flip = uart_flip - self._is_target = True - self._uart = None - self._uart_interval = uart_interval - self._debug_enabled = debug_enabled + split_flip: bool = True, + split_side: Optional[int] = None, + split_type: int = SplitType.UART, + split_target_left: bool = True, + uart_interval: int = 20, + data_pin: Optional[Any] = None, + data_pin2: Optional[Any] = None, + target_left: bool = True, + uart_flip: bool = True, + debug_enabled: bool = False, + ) -> None: + self._is_target: bool = True + self._uart_buffer: UartBuffer = [] + self.split_flip: bool = split_flip + self.split_side: Optional[int] = split_side + self.split_type: int = split_type + self.split_target_left: bool = split_target_left + self.split_offset: Optional[int] = None + self.data_pin: Optional[Any] = data_pin + self.data_pin2: Optional[Any] = data_pin2 + self.target_left: bool = target_left + self.uart_flip: bool = uart_flip + self._is_target: bool = True + self._uart: Optional[busio.UART] = None + self._uart_interval: int = uart_interval + self._debug_enabled: bool = debug_enabled if self.split_type == SplitType.BLE: try: - from adafruit_ble import BLERadio + from adafruit_ble import BLEConnection, BLERadio from adafruit_ble.advertising.standard import ( ProvideServicesAdvertisement, ) from adafruit_ble.services.nordic import UARTService - self.ProvideServicesAdvertisement = ProvideServicesAdvertisement - self.UARTService = UARTService + self.ProvideServicesAdvertisement: Type[ + ProvideServicesAdvertisement + ] = ProvideServicesAdvertisement + self.UARTService: Type[UARTService] = UARTService except ImportError: print('BLE Import error') return # BLE isn't supported on this platform - self._ble = BLERadio() - self._ble_last_scan = ticks_ms() - 5000 - self._connection_count = 0 - self._uart_connection = None - self._advertisment = None - self._advertising = False - self._psave_enable = False + self._ble: BLERadio = BLERadio() + self._ble_last_scan: float = ticks_ms() - 5000 + self._connection_count: int = 0 + self._uart_connection: Optional[BLEConnection] = None + self._advertisment: Optional[ProvideServicesAdvertisement] = None + self._advertising: bool = False + self._psave_enable: bool = False - def during_bootup(self, keyboard): + def during_bootup(self, keyboard: KMKKeyboard) -> None: # Set up name for target side detection and BLE advertisment - name = str(getmount('/').label) + name: str = str(getmount('/').label) if self.split_type == SplitType.BLE: self._ble.name = name else: @@ -127,7 +133,7 @@ class Split(Module): for cidx in range(cols_to_calc): keyboard.coord_mapping.append(intify_coordinate(ridx, cidx)) - def before_matrix_scan(self, keyboard): + def before_matrix_scan(self, keyboard: KMKKeyboard) -> None: if self.split_type == SplitType.BLE: self._check_all_connections() self._receive_ble(keyboard) @@ -138,7 +144,7 @@ class Split(Module): pass # Protocol needs written return - def after_matrix_scan(self, keyboard): + def after_matrix_scan(self, keyboard: KMKKeyboard) -> None: if keyboard.matrix_update: if self.split_type == SplitType.UART and self._is_target: pass # explicit pass just for dev sanity... @@ -155,28 +161,28 @@ class Split(Module): return - def before_hid_send(self, keyboard): + def before_hid_send(self, keyboard: KMKKeyboard) -> None: if not self._is_target: keyboard.hid_pending = False return - def after_hid_send(self, keyboard): + def after_hid_send(self, keyboard: KMKKeyboard) -> None: return - def on_powersave_enable(self, keyboard): + def on_powersave_enable(self, keyboard: KMKKeyboard) -> None: if self.split_type == SplitType.BLE: if self._uart_connection and not self._psave_enable: self._uart_connection.connection_interval = self._uart_interval self._psave_enable = True - def on_powersave_disable(self, keyboard): + def on_powersave_disable(self, keyboard: KMKKeyboard) -> None: if self.split_type == SplitType.BLE: if self._uart_connection and self._psave_enable: self._uart_connection.connection_interval = 11.25 self._psave_enable = False - def _check_all_connections(self): + def _check_all_connections(self) -> None: '''Validates the correct number of BLE connections''' self._connection_count = len(self._ble.connections) if self._is_target and self._connection_count < 2: @@ -184,7 +190,7 @@ class Split(Module): elif not self._is_target and self._connection_count < 1: self._initiator_scan() - def _initiator_scan(self): + def _initiator_scan(self) -> None: '''Scans for target device''' self._uart = None self._uart_connection = None @@ -217,7 +223,7 @@ class Split(Module): break self._ble.stop_scan() - def _target_advertise(self): + def _target_advertise(self) -> None: '''Advertises the target for the initiator to find''' self._ble.stop_advertising() if self._debug_enabled: @@ -239,15 +245,15 @@ class Split(Module): break self._ble.stop_advertising() - def ble_rescan_timer(self): + def ble_rescan_timer(self) -> bool: '''If true, the rescan timer is up''' return bool(ticks_diff(ticks_ms(), self._ble_last_scan) > 5000) - def ble_time_reset(self): + def ble_time_reset(self) -> None: '''Resets the rescan timer''' self._ble_last_scan = ticks_ms() - def _send_ble(self, update): + def _send_ble(self, update: List) -> None: if self._uart: try: if not self._is_target: @@ -265,7 +271,7 @@ class Split(Module): self._uart_connection = None self._uart = None - def _receive_ble(self, keyboard): + def _receive_ble(self, keyboard: KMKKeyboard) -> None: if self._uart is not None and self._uart.in_waiting > 0 or self._uart_buffer: while self._uart.in_waiting >= 3: self._uart_buffer.append(self._uart.read(3)) @@ -273,7 +279,7 @@ class Split(Module): keyboard.secondary_matrix_update = bytearray(self._uart_buffer.pop(0)) return - def _send_uart(self, update): + def _send_uart(self, update: List) -> None: # Change offsets depending on where the data is going to match the correct # matrix location of the receiever if self._is_target: @@ -290,7 +296,7 @@ class Split(Module): if self._uart is not None: self._uart.write(update) - def _receive_uart(self, keyboard): + def _receive_uart(self, keyboard: KMKKeyboard) -> None: if self._uart is not None and self._uart.in_waiting > 0 or self._uart_buffer: if self._uart.in_waiting >= 60: # This is a dirty hack to prevent crashes in unrealistic cases diff --git a/pyproject.toml b/pyproject.toml index 08597ee..2f03e83 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,3 +31,4 @@ exclude = [ "user_keymaps" ] reportMissingModuleSource = false +reportMissingTypeStubs = true From 2c69d0e197e6392fe701874b2ede97573150f070 Mon Sep 17 00:00:00 2001 From: sofubi Date: Fri, 13 Aug 2021 23:10:43 -0400 Subject: [PATCH 04/11] Start work on types Bring in typings folder for adafruit_ble and micropython --- kmk/types.py | 29 ++- typings/adafruit_ble/__init__.pyi | 208 ++++++++++++++++++ typings/adafruit_ble/advertising/__init__.pyi | 173 +++++++++++++++ typings/adafruit_ble/advertising/standard.pyi | 122 ++++++++++ typings/adafruit_ble/attributes/__init__.pyi | 55 +++++ .../adafruit_ble/characteristics/__init__.pyi | 120 ++++++++++ .../adafruit_ble/characteristics/stream.pyi | 49 +++++ typings/adafruit_ble/services/__init__.pyi | 36 +++ typings/adafruit_ble/services/nordic.pyi | 77 +++++++ typings/adafruit_ble/uuid/__init__.pyi | 48 ++++ typings/micropython.pyi | 5 + 11 files changed, 910 insertions(+), 12 deletions(-) create mode 100644 typings/adafruit_ble/__init__.pyi create mode 100644 typings/adafruit_ble/advertising/__init__.pyi create mode 100644 typings/adafruit_ble/advertising/standard.pyi create mode 100644 typings/adafruit_ble/attributes/__init__.pyi create mode 100644 typings/adafruit_ble/characteristics/__init__.pyi create mode 100644 typings/adafruit_ble/characteristics/stream.pyi create mode 100644 typings/adafruit_ble/services/__init__.pyi create mode 100644 typings/adafruit_ble/services/nordic.pyi create mode 100644 typings/adafruit_ble/uuid/__init__.pyi create mode 100644 typings/micropython.pyi diff --git a/kmk/types.py b/kmk/types.py index 54a49b3..5633605 100644 --- a/kmk/types.py +++ b/kmk/types.py @@ -1,3 +1,8 @@ +from kmk.consts import UnicodeMode +from kmk.keys import KeyAttrDict +from typing import List, Optional + + class AttrDict(dict): ''' Primitive support for accessing dictionary entries in dot notation. @@ -7,34 +12,34 @@ class AttrDict(dict): This is read-only on purpose. ''' - def __getattr__(self, key): + def __getattr__(self, key: str) -> str: return self[key] class LayerKeyMeta: - def __init__(self, layer, kc=None): - self.layer = layer - self.kc = kc + def __init__(self, layer: int, kc: Optional[KeyAttrDict] = None) -> None: + self.layer: int = layer + self.kc: Optional[KeyAttrDict] = kc class ModTapKeyMeta: - def __init__(self, kc=None, mods=None): - self.mods = mods - self.kc = kc + def __init__(self, kc: Optional[KeyAttrDict] = None, mods: Optional[List[KeyAttrDict]] = None): + self.mods: Optional[List[KeyAttrDict]] = mods + self.kc: Optional[KeyAttrDict] = kc class KeySequenceMeta: - def __init__(self, seq): - self.seq = seq + def __init__(self, seq: List[KeyAttrDict]): + self.seq: List[KeyAttrDict] = seq class KeySeqSleepMeta: - def __init__(self, ms): - self.ms = ms + def __init__(self, ms: float): + self.ms: float = ms class UnicodeModeKeyMeta: - def __init__(self, mode): + def __init__(self, mode: UnicodeMode): self.mode = mode diff --git a/typings/adafruit_ble/__init__.pyi b/typings/adafruit_ble/__init__.pyi new file mode 100644 index 0000000..bb8377c --- /dev/null +++ b/typings/adafruit_ble/__init__.pyi @@ -0,0 +1,208 @@ +""" +This type stub file was generated by pyright. +""" + +import sys +import _bleio +from .services import Service +from .advertising import Advertisement + +""" + +This module provides higher-level BLE (Bluetooth Low Energy) functionality, +building on the native `_bleio` module. + +""" +if sys.implementation.name == "circuitpython" and sys.implementation.version[0] <= 4: + ... +__version__ = ... +__repo__ = ... +class BLEConnection: + """ + Represents a connection to a peer BLE device. + It acts as a map from a `Service` type to a `Service` instance for the connection. + + :param bleio_connection _bleio.Connection: the native `_bleio.Connection` object to wrap + + """ + def __init__(self, bleio_connection) -> None: + ... + + def __contains__(self, key): # -> bool: + """ + Allows easy testing for a particular Service class or a particular UUID + associated with this connection. + + Example:: + + if UARTService in connection: + # do something + + if StandardUUID(0x1234) in connection: + # do something + """ + ... + + def __getitem__(self, key): # -> None: + """Return the Service for the given Service class or uuid, if any.""" + ... + + @property + def connected(self): + """True if the connection to the peer is still active.""" + ... + + @property + def paired(self): + """True if the paired to the peer.""" + ... + + @property + def connection_interval(self): + """Time between transmissions in milliseconds. Will be multiple of 1.25ms. Lower numbers + increase speed and decrease latency but increase power consumption. + + When setting connection_interval, the peer may reject the new interval and + `connection_interval` will then remain the same. + + Apple has additional guidelines that dictate should be a multiple of 15ms except if HID + is available. When HID is available Apple devices may accept 11.25ms intervals.""" + ... + + @connection_interval.setter + def connection_interval(self, value): # -> None: + ... + + def pair(self, *, bond=...): + """Pair to the peer to increase security of the connection.""" + ... + + def disconnect(self): # -> None: + """Disconnect from peer.""" + ... + + + +class BLERadio: + """ + BLERadio provides the interfaces for BLE advertising, + scanning for advertisements, and connecting to peers. There may be + multiple connections active at once. + + It uses this library's `Advertisement` classes and the `BLEConnection` class.""" + def __init__(self, adapter=...) -> None: + """If no adapter is supplied, use the built-in `_bleio.adapter`. + If no built-in adapter is available, raise `RuntimeError`. + """ + ... + + def start_advertising(self, advertisement, scan_response=..., interval=..., timeout=...): # -> None: + """ + Starts advertising the given advertisement. + + :param buf scan_response: scan response data packet bytes. + If ``None``, a default scan response will be generated that includes + `BLERadio.name` and `BLERadio.tx_power`. + :param float interval: advertising interval, in seconds + :param int timeout: advertising timeout in seconds. + If None, no timeout. + + ``timeout`` is not available in CircuitPython 5.x and must be `None`. + """ + ... + + def stop_advertising(self): # -> None: + """Stops advertising.""" + ... + + def start_scan(self, *advertisement_types, buffer_size=..., extended=..., timeout=..., interval=..., window=..., minimum_rssi=..., active=...): # -> Generator[Advertisement | Unknown, None, None]: + """ + Starts scanning. Returns an iterator of advertisement objects of the types given in + advertisement_types. The iterator will block until an advertisement is heard or the scan + times out. + + If any ``advertisement_types`` are given, only Advertisements of those types are produced + by the returned iterator. If none are given then `Advertisement` objects will be + returned. + + Advertisements and scan responses are filtered and returned separately. + + :param int buffer_size: the maximum number of advertising bytes to buffer. + :param bool extended: When True, support extended advertising packets. + Increasing buffer_size is recommended when this is set. + :param float timeout: the scan timeout in seconds. + If None, will scan until `stop_scan` is called. + :param float interval: the interval (in seconds) between the start + of two consecutive scan windows + Must be in the range 0.0025 - 40.959375 seconds. + :param float window: the duration (in seconds) to scan a single BLE channel. + window must be <= interval. + :param int minimum_rssi: the minimum rssi of entries to return. + :param bool active: request and retrieve scan responses for scannable advertisements. + :return: If any ``advertisement_types`` are given, + only Advertisements of those types are produced by the returned iterator. + If none are given then `Advertisement` objects will be returned. + :rtype: iterable + """ + ... + + def stop_scan(self): # -> None: + """Stops any active scan. + + The scan results iterator will return any buffered results and then raise StopIteration + once empty.""" + ... + + def connect(self, advertisement, *, timeout=...): + """ + Initiates a `BLEConnection` to the peer that advertised the given advertisement. + + :param advertisement Advertisement: An `Advertisement` or a subclass of `Advertisement` + :param timeout float: how long to wait for a connection + :return: the connection to the peer + :rtype: BLEConnection + """ + ... + + @property + def connected(self): # -> bool: + """True if any peers are connected.""" + ... + + @property + def connections(self): # -> tuple[None, ...]: + """A tuple of active `BLEConnection` objects.""" + ... + + @property + def name(self): # -> str: + """The name for this device. Used in advertisements and + as the Device Name in the Generic Access Service, available to a connected peer. + """ + ... + + @name.setter + def name(self, value): # -> None: + ... + + @property + def tx_power(self): # -> Literal[0]: + """Transmit power, in dBm.""" + ... + + @tx_power.setter + def tx_power(self, value): + ... + + @property + def address_bytes(self): # -> bytes: + """The device address, as a ``bytes()`` object of length 6.""" + ... + + @property + def advertising(self): # -> bool: + """The advertising state""" + ... + + + diff --git a/typings/adafruit_ble/advertising/__init__.pyi b/typings/adafruit_ble/advertising/__init__.pyi new file mode 100644 index 0000000..0180bab --- /dev/null +++ b/typings/adafruit_ble/advertising/__init__.pyi @@ -0,0 +1,173 @@ +""" +This type stub file was generated by pyright. +""" + +import struct + +""" +Advertising is the first phase of BLE where devices can broadcast +""" +def to_hex(seq): # -> str: + """Pretty prints a byte sequence as hex values.""" + ... + +def to_bytes_literal(seq): # -> str: + """Prints a byte sequence as a Python bytes literal that only uses hex encoding.""" + ... + +def decode_data(data, *, key_encoding=...): # -> dict[Unknown, Unknown]: + """Helper which decodes length encoded structures into a dictionary with the given key + encoding.""" + ... + +def compute_length(data_dict, *, key_encoding=...): # -> int: + """Computes the length of the encoded data dictionary.""" + ... + +def encode_data(data_dict, *, key_encoding=...): # -> bytes: + """Helper which encodes dictionaries into length encoded structures with the given key + encoding.""" + ... + +class AdvertisingDataField: + """Top level class for any descriptor classes that live in Advertisement or its subclasses.""" + ... + + +class AdvertisingFlag: + """A single bit flag within an AdvertisingFlags object.""" + def __init__(self, bit_position) -> None: + ... + + def __get__(self, obj, cls): # -> AdvertisingFlag: + ... + + def __set__(self, obj, value): # -> None: + ... + + + +class AdvertisingFlags(AdvertisingDataField): + """Standard advertising flags""" + limited_discovery = ... + general_discovery = ... + le_only = ... + def __init__(self, advertisement, advertising_data_type) -> None: + ... + + def __len__(self): # -> Literal[1]: + ... + + def __bytes__(self): # -> bytes: + ... + + def __str__(self) -> str: + ... + + + +class String(AdvertisingDataField): + """UTF-8 encoded string in an Advertisement. + + Not null terminated once encoded because length is always transmitted.""" + def __init__(self, *, advertising_data_type) -> None: + ... + + def __get__(self, obj, cls): # -> String | str | None: + ... + + def __set__(self, obj, value): # -> None: + ... + + + +class Struct(AdvertisingDataField): + """`struct` encoded data in an Advertisement.""" + def __init__(self, struct_format, *, advertising_data_type) -> None: + ... + + def __get__(self, obj, cls): # -> Struct | Any | None: + ... + + def __set__(self, obj, value): # -> None: + ... + + + +class LazyObjectField(AdvertisingDataField): + """Non-data descriptor useful for lazily binding a complex object to an advertisement object.""" + def __init__(self, cls, attribute_name, *, advertising_data_type, **kwargs) -> None: + ... + + def __get__(self, obj, cls): # -> LazyObjectField | None: + ... + + @property + def advertising_data_type(self): + """Return the data type value used to indicate this field.""" + ... + + + +class Advertisement: + """Core Advertisement type. + + The class attribute ``match_prefixes``, if not ``None``, is a tuple of + bytestring prefixes to match against the multiple data structures in the advertisement. + """ + match_prefixes = ... + _prefix_bytes = ... + flags = ... + short_name = ... + complete_name = ... + tx_power = ... + appearance = ... + def __init__(self, *, entry=...) -> None: + """Create an empty advertising packet or one from a ScanEntry.""" + ... + + @property + def rssi(self): # -> None: + """Signal strength of the scanned advertisement. Only available on Advertisements returned + from `BLERadio.start_scan()`. (read-only)""" + ... + + @classmethod + def get_prefix_bytes(cls): # -> bytes | Any: + """Return a merged version of match_prefixes as a single bytes object, + with length headers. + """ + ... + + @classmethod + def matches(cls, entry): + """Returns ``True`` if the given `_bleio.ScanEntry` advertisement fields + matches all of the given prefixes in the `match_prefixes` tuple attribute. + Subclasses may override this to match any instead of all. + """ + ... + + @classmethod + def matches_prefixes(cls, entry, *, all_): + """Returns ``True`` if the given `_bleio.ScanEntry` advertisement fields + match any or all of the given prefixes in the `match_prefixes` tuple attribute. + If ``all_`` is ``True``, all the prefixes must match. If ``all_`` is ``False``, + returns ``True`` if at least one of the prefixes match. + """ + ... + + def __bytes__(self): # -> bytes: + """The raw packet bytes.""" + ... + + def __str__(self) -> str: + ... + + def __len__(self): # -> int: + ... + + def __repr__(self): # -> str: + ... + + + diff --git a/typings/adafruit_ble/advertising/standard.pyi b/typings/adafruit_ble/advertising/standard.pyi new file mode 100644 index 0000000..9556d96 --- /dev/null +++ b/typings/adafruit_ble/advertising/standard.pyi @@ -0,0 +1,122 @@ +""" +This type stub file was generated by pyright. +""" + +from . import Advertisement, AdvertisingDataField + +""" +:py:mod:`~adafruit_ble.advertising.standard` +==================================================== + +This module provides BLE standard defined advertisements. The Advertisements are single purpose +even though multiple purposes may actually be present in a single packet. + +""" +__version__ = ... +__repo__ = ... +class BoundServiceList: + """Sequence-like object of Service UUID objects. It stores both standard and vendor UUIDs.""" + def __init__(self, advertisement, *, standard_services, vendor_services) -> None: + ... + + def __contains__(self, key): # -> bool: + ... + + def __iter__(self): # -> Iterator[Unknown]: + ... + + def append(self, service): # -> None: + """Append a service to the list.""" + ... + + def extend(self, services): # -> None: + """Appends all services in the iterable to the list.""" + ... + + def __str__(self) -> str: + ... + + + +class ServiceList(AdvertisingDataField): + """Descriptor for a list of Service UUIDs that lazily binds a corresponding BoundServiceList.""" + def __init__(self, *, standard_services, vendor_services) -> None: + ... + + def __get__(self, obj, cls): # -> ServiceList | tuple[()]: + ... + + + +class ProvideServicesAdvertisement(Advertisement): + """Advertise what services that the device makes available upon connection.""" + match_prefixes = ... + services = ... + def __init__(self, *services, entry=...) -> None: + ... + + @classmethod + def matches(cls, entry): + """Only one kind of service list need be present in a ProvideServicesAdvertisement, + so override the default behavior and match any prefix, not all. + """ + ... + + + +class SolicitServicesAdvertisement(Advertisement): + """Advertise what services the device would like to use over a connection.""" + match_prefixes = ... + solicited_services = ... + def __init__(self, *services, entry=...) -> None: + ... + + + +class ManufacturerData(AdvertisingDataField): + """Encapsulates manufacturer specific keyed data bytes. The manufacturer is identified by the + company_id and the data is structured like an advertisement with a configurable key + format. The order of the serialized data is determined by the order that the + `ManufacturerDataField` attributes are set in - this can be useful for + `match_prefixes` in an `Advertisement` sub-class.""" + def __init__(self, obj, *, advertising_data_type=..., company_id, key_encoding=...) -> None: + ... + + def __len__(self): # -> int: + ... + + def __bytes__(self): # -> bytes: + ... + + def __str__(self) -> str: + ... + + + +class ManufacturerDataField: + """A single piece of data within the manufacturer specific data. The format can be repeated.""" + def __init__(self, key, value_format, field_names=...) -> None: + ... + + def __get__(self, obj, cls): # -> ManufacturerDataField | mdf_tuple | Any | Tuple[Any, ...] | tuple[None, ...] | None: + ... + + def __set__(self, obj, value): # -> None: + ... + + + +class ServiceData(AdvertisingDataField): + """Encapsulates service data. It is read as a memoryview which can be manipulated or set as a + bytearray to change the size.""" + def __init__(self, service) -> None: + ... + + def __get__(self, obj, cls): # -> ServiceData | memoryview | None: + ... + + def __set__(self, obj, value): # -> None: + ... + + + diff --git a/typings/adafruit_ble/attributes/__init__.pyi b/typings/adafruit_ble/attributes/__init__.pyi new file mode 100644 index 0000000..03c7968 --- /dev/null +++ b/typings/adafruit_ble/attributes/__init__.pyi @@ -0,0 +1,55 @@ +""" +This type stub file was generated by pyright. +""" + +import _bleio + +""" +:py:mod:`~adafruit_ble.attributes` +==================================================== + +This module provides definitions common to all kinds of BLE attributes, +specifically characteristics and descriptors. + +""" +__version__ = ... +__repo__ = ... +class Attribute: + """Constants describing security levels. + + .. data:: NO_ACCESS + + security mode: access not allowed + + .. data:: OPEN + + security_mode: no security (link is not encrypted) + + .. data:: ENCRYPT_NO_MITM + + security_mode: unauthenticated encryption, without man-in-the-middle protection + + .. data:: ENCRYPT_WITH_MITM + + security_mode: authenticated encryption, with man-in-the-middle protection + + .. data:: LESC_ENCRYPT_WITH_MITM + + security_mode: LESC encryption, with man-in-the-middle protection + + .. data:: SIGNED_NO_MITM + + security_mode: unauthenticated data signing, without man-in-the-middle protection + + .. data:: SIGNED_WITH_MITM + + security_mode: authenticated data signing, without man-in-the-middle protection""" + NO_ACCESS = ... + OPEN = ... + ENCRYPT_NO_MITM = ... + ENCRYPT_WITH_MITM = ... + LESC_ENCRYPT_WITH_MITM = ... + SIGNED_NO_MITM = ... + SIGNED_WITH_MITM = ... + + diff --git a/typings/adafruit_ble/characteristics/__init__.pyi b/typings/adafruit_ble/characteristics/__init__.pyi new file mode 100644 index 0000000..b368038 --- /dev/null +++ b/typings/adafruit_ble/characteristics/__init__.pyi @@ -0,0 +1,120 @@ +""" +This type stub file was generated by pyright. +""" + +import struct +import _bleio +from ..attributes import Attribute + +""" + +This module provides core BLE characteristic classes that are used within Services. + +""" +__version__ = ... +__repo__ = ... +class Characteristic: + """ + Top level Characteristic class that does basic binding. + + :param UUID uuid: The uuid of the characteristic + :param int properties: The properties of the characteristic, + specified as a bitmask of these values bitwise-or'd together: + `BROADCAST`, `INDICATE`, `NOTIFY`, `READ`, `WRITE`, `WRITE_NO_RESPONSE`. + :param int read_perm: Specifies whether the characteristic can be read by a client, + and if so, which security mode is required. + Must be one of the integer values `Attribute.NO_ACCESS`, `Attribute.OPEN`, + `Attribute.ENCRYPT_NO_MITM`, `Attribute.ENCRYPT_WITH_MITM`, + `Attribute.LESC_ENCRYPT_WITH_MITM`, + `Attribute.SIGNED_NO_MITM`, or `Attribute.SIGNED_WITH_MITM`. + :param int write_perm: Specifies whether the characteristic can be written by a client, + and if so, which security mode is required. Values allowed are the same as ``read_perm``. + :param int max_length: Maximum length in bytes of the characteristic value. The maximum allowed + by the BLE specification is 512. On nRF, if ``fixed_length`` is ``True``, the maximum + is 510. The default value is 20, which is the maximum + number of data bytes that fit in a single BLE 4.x ATT packet. + :param bool fixed_length: True if the characteristic value is of fixed length. + :param buf initial_value: The initial value for this characteristic. If not given, will be + filled with zeros. + + .. data:: BROADCAST + + property: allowed in advertising packets + + .. data:: INDICATE + + property: server will indicate to the client when the value is set and wait for a response + + .. data:: NOTIFY + + property: server will notify the client when the value is set + + .. data:: READ + + property: clients may read this characteristic + + .. data:: WRITE + + property: clients may write this characteristic; a response will be sent back + + .. data:: WRITE_NO_RESPONSE + + property: clients may write this characteristic; no response will be sent back""" + BROADCAST = ... + INDICATE = ... + NOTIFY = ... + READ = ... + WRITE = ... + WRITE_NO_RESPONSE = ... + def __init__(self, *, uuid=..., properties=..., read_perm=..., write_perm=..., max_length=..., fixed_length=..., initial_value=...) -> None: + ... + + def __get__(self, service, cls=...): # -> Characteristic: + ... + + def __set__(self, service, value): # -> None: + ... + + + +class ComplexCharacteristic: + """ + Characteristic class that does complex binding where the subclass returns a full object for + interacting with the characteristic data. The Characteristic itself will be shadowed once it + has been bound to the corresponding instance attribute. + """ + def __init__(self, *, uuid=..., properties=..., read_perm=..., write_perm=..., max_length=..., fixed_length=..., initial_value=...) -> None: + ... + + def bind(self, service): + """Binds the characteristic to the local Service or remote Characteristic object given.""" + ... + + def __get__(self, service, cls=...): # -> ComplexCharacteristic: + ... + + + +class StructCharacteristic(Characteristic): + """ + Data descriptor for a structure with a fixed format. + + :param struct_format: a `struct` format string describing how to pack multiple values + into the characteristic bytestring + :param UUID uuid: The uuid of the characteristic + :param int properties: see `Characteristic` + :param int read_perm: see `Characteristic` + :param int write_perm: see `Characteristic` + :param buf initial_value: see `Characteristic` + """ + def __init__(self, struct_format, *, uuid=..., properties=..., read_perm=..., write_perm=..., initial_value=...) -> None: + ... + + def __get__(self, obj, cls=...): # -> StructCharacteristic | Tuple[Any, ...] | None: + ... + + def __set__(self, obj, value): # -> None: + ... + + + diff --git a/typings/adafruit_ble/characteristics/stream.pyi b/typings/adafruit_ble/characteristics/stream.pyi new file mode 100644 index 0000000..d2c64b0 --- /dev/null +++ b/typings/adafruit_ble/characteristics/stream.pyi @@ -0,0 +1,49 @@ +""" +This type stub file was generated by pyright. +""" + +from . import ComplexCharacteristic + +""" +`stream` +==================================================== + +This module provides stream characteristics that bind readable or writable objects to the Service +object they are on. + +""" +__version__ = ... +__repo__ = ... +class BoundWriteStream: + """Writes data out to the peer.""" + def __init__(self, bound_characteristic) -> None: + ... + + def write(self, buf): # -> None: + """Write data from buf out to the peer.""" + ... + + + +class StreamOut(ComplexCharacteristic): + """Output stream from the Service server.""" + def __init__(self, *, uuid=..., timeout=..., buffer_size=..., properties=..., read_perm=..., write_perm=...) -> None: + ... + + def bind(self, service): # -> CharacteristicBuffer | BoundWriteStream: + """Binds the characteristic to the given Service.""" + ... + + + +class StreamIn(ComplexCharacteristic): + """Input stream into the Service server.""" + def __init__(self, *, uuid=..., timeout=..., buffer_size=..., properties=..., write_perm=...) -> None: + ... + + def bind(self, service): # -> BoundWriteStream | CharacteristicBuffer: + """Binds the characteristic to the given Service.""" + ... + + + diff --git a/typings/adafruit_ble/services/__init__.pyi b/typings/adafruit_ble/services/__init__.pyi new file mode 100644 index 0000000..efd308a --- /dev/null +++ b/typings/adafruit_ble/services/__init__.pyi @@ -0,0 +1,36 @@ +""" +This type stub file was generated by pyright. +""" + +import _bleio +from ..characteristics import Characteristic, ComplexCharacteristic + +""" + +This module provides the top level Service definition. + +""" +__version__ = ... +__repo__ = ... +class Service: + """Top level Service class that handles the hard work of binding to a local or remote service. + + Providers of a local service should instantiate their Service with service=None, the default. + The local Service's characteristics will be lazily made available to clients as they are used + locally. In other words, a characteristic won't be available to remote clients until it has + been read or written locally. + + To use a remote Service, get the item with the key of the Service type on the + `BLEConnection`. For example, ``connection[UartService]`` will return the UartService + instance for the connection's peer. + """ + def __init__(self, *, service=..., secondary=..., **initial_values) -> None: + ... + + @property + def remote(self): # -> bool: + """True if the service is provided by a peer and accessed remotely.""" + ... + + + diff --git a/typings/adafruit_ble/services/nordic.pyi b/typings/adafruit_ble/services/nordic.pyi new file mode 100644 index 0000000..aef9441 --- /dev/null +++ b/typings/adafruit_ble/services/nordic.pyi @@ -0,0 +1,77 @@ +""" +This type stub file was generated by pyright. +""" + +from . import Service + +""" +`nordic` +==================================================== + +This module provides Services used by Nordic Semiconductors. + +""" +__version__ = ... +__repo__ = ... +class UARTService(Service): + """ + Provide UART-like functionality via the Nordic NUS service. + + :param int timeout: the timeout in seconds to wait + for the first character and between subsequent characters. + :param int buffer_size: buffer up to this many bytes. + If more bytes are received, older bytes will be discarded. + + See ``examples/ble_uart_echo_test.py`` for a usage example. + """ + uuid = ... + _server_tx = ... + _server_rx = ... + def __init__(self, service=...) -> None: + ... + + def read(self, nbytes=...): + """ + Read characters. If ``nbytes`` is specified then read at most that many bytes. + Otherwise, read everything that arrives until the connection times out. + Providing the number of bytes expected is highly recommended because it will be faster. + + :return: Data read + :rtype: bytes or None + """ + ... + + def readinto(self, buf, nbytes=...): + """ + Read bytes into the ``buf``. If ``nbytes`` is specified then read at most + that many bytes. Otherwise, read at most ``len(buf)`` bytes. + + :return: number of bytes read and stored into ``buf`` + :rtype: int or None (on a non-blocking error) + """ + ... + + def readline(self): + """ + Read a line, ending in a newline character. + + :return: the line read + :rtype: bytes or None + """ + ... + + @property + def in_waiting(self): + """The number of bytes in the input buffer, available to be read.""" + ... + + def reset_input_buffer(self): # -> None: + """Discard any unread characters in the input buffer.""" + ... + + def write(self, buf): # -> None: + """Write a buffer of bytes.""" + ... + + + diff --git a/typings/adafruit_ble/uuid/__init__.pyi b/typings/adafruit_ble/uuid/__init__.pyi new file mode 100644 index 0000000..7f68c63 --- /dev/null +++ b/typings/adafruit_ble/uuid/__init__.pyi @@ -0,0 +1,48 @@ +""" +This type stub file was generated by pyright. +""" + +import struct +import _bleio + +""" + +This module provides core Unique ID (UUID) classes. + +""" +__version__ = ... +__repo__ = ... +class UUID: + """Top level UUID""" + def __hash__(self) -> int: + ... + + def __eq__(self, other) -> bool: + ... + + def __str__(self) -> str: + ... + + def __bytes__(self): # -> bytes: + ... + + def pack_into(self, buffer, offset=...): # -> None: + """Packs the UUID into the buffer at the given offset.""" + ... + + + +class StandardUUID(UUID): + """Standard 16-bit UUID defined by the Bluetooth SIG.""" + def __init__(self, uuid16) -> None: + ... + + + +class VendorUUID(UUID): + """Vendor defined, 128-bit UUID.""" + def __init__(self, uuid128) -> None: + ... + + + diff --git a/typings/micropython.pyi b/typings/micropython.pyi new file mode 100644 index 0000000..f2c1dc4 --- /dev/null +++ b/typings/micropython.pyi @@ -0,0 +1,5 @@ +from typing import TypeVar + +T = TypeVar('T') + +def const(x: T) -> T: ... From 75f9d10cc7c20251bb6b51662dee4306544693c7 Mon Sep 17 00:00:00 2001 From: sofubi Date: Sun, 15 Aug 2021 21:23:02 +0000 Subject: [PATCH 05/11] Add types to validators, time, types Update pyproject.toml to ignore boards, user_keymaps for mypy Update pyproject.toml to reduce loud mypy reporting --- kmk/key_validators.py | 2 +- kmk/kmktime.py | 10 +++++----- kmk/types.py | 9 +++++---- pyproject.toml | 9 ++++++++- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/kmk/key_validators.py b/kmk/key_validators.py index 71af33d..e150412 100644 --- a/kmk/key_validators.py +++ b/kmk/key_validators.py @@ -7,7 +7,7 @@ from kmk.types import ( ) -def key_seq_sleep_validator(ms): +def key_seq_sleep_validator(ms: float) -> KeySeqSleepMeta: return KeySeqSleepMeta(ms) diff --git a/kmk/kmktime.py b/kmk/kmktime.py index 18b62a8..60e724d 100644 --- a/kmk/kmktime.py +++ b/kmk/kmktime.py @@ -1,23 +1,23 @@ import time -def sleep_ms(ms): +def sleep_ms(ms: float) -> None: return time.sleep(ms / 1000) -def ticks_ms(): +def ticks_ms() -> float: '''Has .25s granularity, but is cheap''' return time.monotonic() * 1000 -def ticks_diff(new, old): +def ticks_diff(new: float, old: float) -> float: return new - old -def accurate_ticks(): +def accurate_ticks() -> int: '''Is more expensive, but good for time critical things''' return time.monotonic_ns() -def accurate_ticks_diff(new, old, ms): +def accurate_ticks_diff(new: float, old: float, ms: float) -> bool: return bool(new - old < ms * 1000000) diff --git a/kmk/types.py b/kmk/types.py index 5633605..a2a0062 100644 --- a/kmk/types.py +++ b/kmk/types.py @@ -1,6 +1,7 @@ +from typing import List, Optional, Tuple + from kmk.consts import UnicodeMode from kmk.keys import KeyAttrDict -from typing import List, Optional class AttrDict(dict): @@ -23,8 +24,8 @@ class LayerKeyMeta: class ModTapKeyMeta: - def __init__(self, kc: Optional[KeyAttrDict] = None, mods: Optional[List[KeyAttrDict]] = None): - self.mods: Optional[List[KeyAttrDict]] = mods + def __init__(self, kc: Optional[KeyAttrDict] = None, mods: Optional[List[KeyAttrDict]] = None) -> None: + self.mods: Optional[List[KeyAttrDict]] = mods self.kc: Optional[KeyAttrDict] = kc @@ -44,5 +45,5 @@ class UnicodeModeKeyMeta: class TapDanceKeyMeta: - def __init__(self, codes): + def __init__(self, codes: Tuple[KeyAttrDict, ...]): self.codes = codes diff --git a/pyproject.toml b/pyproject.toml index 2f03e83..314e76d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,14 @@ include = ["kmk"] exclude = [ "hardware", ".venv", - "user_keymaps" + "user_keymaps", + "boards" ] reportMissingModuleSource = false +# reports missing typestubs allowing for a code action to +# create new library typestubs reportMissingTypeStubs = true + +[tool.mypy] +exclude = "boards/|user_keymaps/" +ignore_missing_imports = true From 8cb2a8b485dd514f087c2445963f9f24bcf18615 Mon Sep 17 00:00:00 2001 From: sofubi Date: Mon, 16 Aug 2021 00:15:09 +0000 Subject: [PATCH 06/11] Type annotate kmk_keyboard.py Update refs from KeyAttrDict to Key in encoder.py Update types.py for validity Complete typing of key_validators.py --- kmk/key_validators.py | 10 +-- kmk/kmk_keyboard.py | 138 +++++++++++++++++++++-------------------- kmk/modules/encoder.py | 6 +- kmk/types.py | 25 ++++---- 4 files changed, 92 insertions(+), 87 deletions(-) diff --git a/kmk/key_validators.py b/kmk/key_validators.py index e150412..52625ab 100644 --- a/kmk/key_validators.py +++ b/kmk/key_validators.py @@ -1,3 +1,5 @@ +from typing import List, Optional, Tuple +from kmk.keys import Key from kmk.types import ( KeySeqSleepMeta, LayerKeyMeta, @@ -11,7 +13,7 @@ def key_seq_sleep_validator(ms: float) -> KeySeqSleepMeta: return KeySeqSleepMeta(ms) -def layer_key_validator(layer, kc=None): +def layer_key_validator(layer: int, kc: Key = None) -> 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 @@ -22,16 +24,16 @@ def layer_key_validator(layer, kc=None): return LayerKeyMeta(layer=layer, kc=kc) -def mod_tap_validator(kc, mods=None): +def mod_tap_validator(kc: Key, mods: Optional[List[Key]] = None) -> ModTapKeyMeta: ''' Validates that mod tap keys are correctly used ''' return ModTapKeyMeta(kc=kc, mods=mods) -def tap_dance_key_validator(*codes): +def tap_dance_key_validator(*codes: Key) -> TapDanceKeyMeta: return TapDanceKeyMeta(codes) -def unicode_mode_key_validator(mode): +def unicode_mode_key_validator(mode: int) -> UnicodeModeKeyMeta: return UnicodeModeKeyMeta(mode) diff --git a/kmk/kmk_keyboard.py b/kmk/kmk_keyboard.py index acb199c..60a0d08 100644 --- a/kmk/kmk_keyboard.py +++ b/kmk/kmk_keyboard.py @@ -1,73 +1,77 @@ +from __future__ import annotations + +from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type, Union + from kmk.consts import KMK_RELEASE, UnicodeMode -from kmk.hid import BLEHID, USBHID, AbstractHID, HIDModes -from kmk.keys import KC +from kmk.hid import AbstractHID, BLEHID, USBHID, 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 class Sandbox: - matrix_update = None - secondary_matrix_update = None - active_layers = None + matrix_update: Optional[bytearray] = None + secondary_matrix_update: Optional[bytearray] = None + active_layers: Optional[List[int]] = None class KMKKeyboard: ##### # User-configurable - debug_enabled = False + debug_enabled: bool = False - keymap = [] - coord_mapping = None + keymap: List[KeyAttrDict] = [] + coord_mapping: Optional[List[int]] = None - row_pins = None - col_pins = None - diode_orientation = None - matrix = None - matrix_scanner = MatrixScanner - uart_buffer = [] + 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] = [] - unicode_mode = UnicodeMode.NOOP - tap_time = 300 + unicode_mode: int = UnicodeMode.NOOP + tap_time: int = 300 - modules = [] - extensions = [] - sandbox = Sandbox() + modules: List[Type[Any]] = [] + extensions: List[Type[Any]] = [] + sandbox: Sandbox = Sandbox() ##### # Internal State - keys_pressed = set() - _coordkeys_pressed = {} - hid_type = HIDModes.USB - secondary_hid_type = None - _hid_helper = None - hid_pending = False - state_layer_key = None - matrix_update = None - secondary_matrix_update = None - _matrix_modify = None - state_changed = False - _old_timeouts_len = None - _new_timeouts_len = None - _trigger_powersave_enable = False - _trigger_powersave_disable = False - i2c_deinit_count = 0 + 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 # 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 = [0] + active_layers: List[int] = [0] - _timeouts = {} - _tapping = False - _tap_dance_counts = {} - _tap_side_effects = {} + _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]] = {} # 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): + def __repr__(self) -> str: return ( 'KMKKeyboard(' 'debug_enabled={} ' @@ -103,22 +107,22 @@ class KMKKeyboard: self._tap_side_effects, ) - def _print_debug_cycle(self, init=False): + def _print_debug_cycle(self, init: bool = False) -> None: if self.debug_enabled: if init: print('KMKInit(release={})'.format(KMK_RELEASE)) print(self) - def _send_hid(self): + def _send_hid(self) -> None: self._hid_helper.create_report(self.keys_pressed).send() self.hid_pending = False - def _handle_matrix_report(self, update=None): + def _handle_matrix_report(self, update: bytearray = None) -> 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, row, col): + def _find_key_in_map(self, int_coord: int, row: int, col: int) -> Union[Key, None]: self.state_layer_key = None try: idx = self.coord_mapping.index(int_coord) @@ -143,7 +147,7 @@ class KMKKeyboard: return self.state_layer_key - def _on_matrix_changed(self, row, col, is_pressed): + def _on_matrix_changed(self, row: int, col: int, is_pressed: int) -> KMKKeyboard: if self.debug_enabled: print('MatrixChange(col={} row={} pressed={})'.format(col, row, is_pressed)) @@ -156,7 +160,7 @@ class KMKKeyboard: return self.process_key(kc_changed, is_pressed, int_coord, (row, col)) - def process_key(self, key, is_pressed, coord_int=None, coord_raw=None): + def process_key(self, key: Union[Key, TapDanceKeyMeta], is_pressed: int, coord_int: Optional[int] = None, coord_raw: Tuple[int, int] = None) -> KMKKeyboard: if self._tapping and not isinstance(key.meta, TapDanceKeyMeta): self._process_tap_dance(key, is_pressed) else: @@ -167,22 +171,22 @@ class KMKKeyboard: return self - def remove_key(self, keycode): + def remove_key(self, keycode: Key) -> KMKKeyboard: self.keys_pressed.discard(keycode) return self.process_key(keycode, False) - def add_key(self, keycode): + def add_key(self, keycode: Key) -> KMKKeyboard: self.keys_pressed.add(keycode) return self.process_key(keycode, True) - def tap_key(self, keycode): + def tap_key(self, keycode: 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, is_pressed): + def _process_tap_dance(self, changed_key: Union[Key, TapDanceKeyMeta], is_pressed: int) -> KMKKeyboard: if is_pressed: if not isinstance(changed_key.meta, TapDanceKeyMeta): # If we get here, changed_key is not a TapDanceKey and thus @@ -219,7 +223,7 @@ class KMKKeyboard: return self - def _end_tap_dance(self, td_key): + def _end_tap_dance(self, td_key: Union[Key, TapDanceKeyMeta]) -> KMKKeyboard: v = self._tap_dance_counts[td_key] - 1 if v >= 0: @@ -240,12 +244,12 @@ class KMKKeyboard: return self - def _cleanup_tap_dance(self, td_key): + def _cleanup_tap_dance(self, td_key: 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, callback): + def set_timeout(self, after_ticks: float, callback: 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() @@ -258,11 +262,11 @@ class KMKKeyboard: self._timeouts[timeout_key] = callback return timeout_key - def _cancel_timeout(self, timeout_key): + def _cancel_timeout(self, timeout_key: float) -> None: if timeout_key in self._timeouts: del self._timeouts[timeout_key] - def _process_timeouts(self): + def _process_timeouts(self) -> KMKKeyboard: if not self._timeouts: return self @@ -279,7 +283,7 @@ class KMKKeyboard: return self - def _init_sanity_check(self): + def _init_sanity_check(self) -> KMKKeyboard: ''' Ensure the provided configuration is *probably* bootable ''' @@ -293,7 +297,7 @@ class KMKKeyboard: return self - def _init_coord_mapping(self): + 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 @@ -315,7 +319,7 @@ class KMKKeyboard: for cidx in range(cols_to_calc): self.coord_mapping.append(intify_coordinate(ridx, cidx)) - def _init_hid(self): + def _init_hid(self) -> None: if self.hid_type == HIDModes.NOOP: self._hid_helper = AbstractHID elif self.hid_type == HIDModes.USB: @@ -326,7 +330,7 @@ class KMKKeyboard: self._hid_helper = AbstractHID self._hid_helper = self._hid_helper() - def _init_matrix(self): + def _init_matrix(self) -> KMKKeyboard: self.matrix = MatrixScanner( cols=self.col_pins, rows=self.row_pins, @@ -336,7 +340,7 @@ class KMKKeyboard: return self - def before_matrix_scan(self): + def before_matrix_scan(self) -> None: for module in self.modules: try: module.before_matrix_scan(self) @@ -351,7 +355,7 @@ class KMKKeyboard: if self.debug_enabled: print('Failed to run pre matrix function in extension: ', err, ext) - def after_matrix_scan(self): + def after_matrix_scan(self) -> None: for module in self.modules: try: module.after_matrix_scan(self) @@ -366,7 +370,7 @@ class KMKKeyboard: if self.debug_enabled: print('Failed to run post matrix function in extension: ', err, ext) - def before_hid_send(self): + def before_hid_send(self) -> None: for module in self.modules: try: module.before_hid_send(self) @@ -381,7 +385,7 @@ class KMKKeyboard: if self.debug_enabled: print('Failed to run pre hid function in extension: ', err, ext) - def after_hid_send(self): + def after_hid_send(self) -> None: for module in self.modules: try: module.after_hid_send(self) @@ -396,7 +400,7 @@ class KMKKeyboard: if self.debug_enabled: print('Failed to run post hid function in extension: ', err, ext) - def powersave_enable(self): + def powersave_enable(self) -> None: for module in self.modules: try: module.on_powersave_enable(self) @@ -411,7 +415,7 @@ class KMKKeyboard: if self.debug_enabled: print('Failed to run post hid function in extension: ', err, ext) - def powersave_disable(self): + def powersave_disable(self) -> None: for module in self.modules: try: module.on_powersave_disable(self) @@ -425,7 +429,7 @@ class KMKKeyboard: if self.debug_enabled: print('Failed to run post hid function in extension: ', err, ext) - def go(self, hid_type=HIDModes.USB, secondary_hid_type=None, **kwargs): + def go(self, hid_type: int = HIDModes.USB, secondary_hid_type: Optional[int] = None, **kwargs: Dict[Any, Any]) -> None: self.hid_type = hid_type self.secondary_hid_type = secondary_hid_type diff --git a/kmk/modules/encoder.py b/kmk/modules/encoder.py index e5f0d30..61d6e3e 100644 --- a/kmk/modules/encoder.py +++ b/kmk/modules/encoder.py @@ -2,14 +2,14 @@ import digitalio from typing import Any, ClassVar, Dict, List, Optional, Tuple, Union -from kmk.keys import KeyAttrDict +from kmk.keys import Key from kmk.kmk_keyboard import KMKKeyboard from kmk.kmktime import ticks_ms from kmk.modules import Module EncoderMap = Tuple[ - List[Tuple[KeyAttrDict, KeyAttrDict, int]], - List[Tuple[KeyAttrDict, KeyAttrDict, int]], + List[Tuple[Key, Key, int]], + List[Tuple[Key, Key, int]], List[Tuple[None, None, int]], ] diff --git a/kmk/types.py b/kmk/types.py index a2a0062..bd8e454 100644 --- a/kmk/types.py +++ b/kmk/types.py @@ -1,7 +1,6 @@ from typing import List, Optional, Tuple -from kmk.consts import UnicodeMode -from kmk.keys import KeyAttrDict +from kmk.keys import Key class AttrDict(dict): @@ -18,20 +17,20 @@ class AttrDict(dict): class LayerKeyMeta: - def __init__(self, layer: int, kc: Optional[KeyAttrDict] = None) -> None: + def __init__(self, layer: int, kc: Optional[Key] = None) -> None: self.layer: int = layer - self.kc: Optional[KeyAttrDict] = kc + self.kc: Optional[Key] = kc class ModTapKeyMeta: - def __init__(self, kc: Optional[KeyAttrDict] = None, mods: Optional[List[KeyAttrDict]] = None) -> None: - self.mods: Optional[List[KeyAttrDict]] = mods - self.kc: Optional[KeyAttrDict] = kc + def __init__(self, kc: Optional[Key] = None, mods: Optional[List[Key]] = None) -> None: + self.mods: Optional[List[Key]] = mods + self.kc: Optional[Key] = kc class KeySequenceMeta: - def __init__(self, seq: List[KeyAttrDict]): - self.seq: List[KeyAttrDict] = seq + def __init__(self, seq: List[Key]): + self.seq: List[Key] = seq class KeySeqSleepMeta: @@ -40,10 +39,10 @@ class KeySeqSleepMeta: class UnicodeModeKeyMeta: - def __init__(self, mode: UnicodeMode): - self.mode = mode + def __init__(self, mode: int): + self.mode: int = mode class TapDanceKeyMeta: - def __init__(self, codes: Tuple[KeyAttrDict, ...]): - self.codes = codes + def __init__(self, codes: Tuple[Key, ...]): + self.codes: Tuple[Key, ...] = codes From a622239b4a72db8f498dc9614ec96f3712ebb725 Mon Sep 17 00:00:00 2001 From: sofubi Date: Mon, 16 Aug 2021 00:18:35 +0000 Subject: [PATCH 07/11] Fix isort and formatting --- kmk/key_validators.py | 1 + kmk/kmk_keyboard.py | 21 ++++++++++++++++---- kmk/modules/encoder.py | 4 +++- kmk/modules/layers.py | 44 +++++++++++++++++++++++++++++++----------- kmk/modules/modtap.py | 8 ++++++-- kmk/modules/power.py | 14 ++++++++++---- kmk/types.py | 4 +++- 7 files changed, 73 insertions(+), 23 deletions(-) diff --git a/kmk/key_validators.py b/kmk/key_validators.py index 52625ab..6e3018a 100644 --- a/kmk/key_validators.py +++ b/kmk/key_validators.py @@ -1,4 +1,5 @@ from typing import List, Optional, Tuple + from kmk.keys import Key from kmk.types import ( KeySeqSleepMeta, diff --git a/kmk/kmk_keyboard.py b/kmk/kmk_keyboard.py index 60a0d08..231d084 100644 --- a/kmk/kmk_keyboard.py +++ b/kmk/kmk_keyboard.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type, Union from kmk.consts import KMK_RELEASE, UnicodeMode -from kmk.hid import AbstractHID, BLEHID, USBHID, HIDModes +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 @@ -160,7 +160,13 @@ class KMKKeyboard: return self.process_key(kc_changed, is_pressed, int_coord, (row, col)) - def process_key(self, key: Union[Key, TapDanceKeyMeta], is_pressed: int, coord_int: Optional[int] = None, coord_raw: Tuple[int, int] = None) -> KMKKeyboard: + def process_key( + self, + key: Union[Key, TapDanceKeyMeta], + is_pressed: int, + coord_int: Optional[int] = None, + coord_raw: Tuple[int, int] = None, + ) -> KMKKeyboard: if self._tapping and not isinstance(key.meta, TapDanceKeyMeta): self._process_tap_dance(key, is_pressed) else: @@ -186,7 +192,9 @@ class KMKKeyboard: return self - def _process_tap_dance(self, changed_key: Union[Key, TapDanceKeyMeta], is_pressed: int) -> KMKKeyboard: + def _process_tap_dance( + self, changed_key: Union[Key, TapDanceKeyMeta], is_pressed: int + ) -> KMKKeyboard: if is_pressed: if not isinstance(changed_key.meta, TapDanceKeyMeta): # If we get here, changed_key is not a TapDanceKey and thus @@ -429,7 +437,12 @@ class KMKKeyboard: if self.debug_enabled: print('Failed to run post hid function in extension: ', err, ext) - def go(self, hid_type: int = HIDModes.USB, secondary_hid_type: Optional[int] = None, **kwargs: Dict[Any, Any]) -> None: + def go( + self, + hid_type: int = HIDModes.USB, + secondary_hid_type: Optional[int] = None, + **kwargs: Dict[Any, Any] + ) -> None: self.hid_type = hid_type self.secondary_hid_type = secondary_hid_type diff --git a/kmk/modules/encoder.py b/kmk/modules/encoder.py index 61d6e3e..fc09ce7 100644 --- a/kmk/modules/encoder.py +++ b/kmk/modules/encoder.py @@ -181,7 +181,9 @@ class Encoder: class EncoderHandler(Module): encoders: ClassVar[List[Encoder]] = [] - debug_enabled: ClassVar[bool] = False # not working as inttended, do not use for now + debug_enabled: ClassVar[ + bool + ] = False # not working as inttended, do not use for now def __init__( self, pad_a: List[Any], pad_b: List[Any], encoder_map: EncoderMap diff --git a/kmk/modules/layers.py b/kmk/modules/layers.py index 477db07..b00f8d6 100644 --- a/kmk/modules/layers.py +++ b/kmk/modules/layers.py @@ -87,20 +87,26 @@ class Layers(Module): def on_powersave_disable(self, keyboard: KMKKeyboard) -> None: return - def _df_pressed(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> None: + def _df_pressed( + self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + ) -> None: ''' Switches the default layer ''' keyboard.active_layers[-1] = key.meta.layer - def _mo_pressed(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> None: + def _mo_pressed( + self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + ) -> None: ''' Momentarily activates layer, switches off when you let go ''' keyboard.active_layers.insert(0, key.meta.layer) @staticmethod - def _mo_released(key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> None: + def _mo_released( + key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + ) -> None: # remove the first instance of the target layer # from the active list # under almost all normal use cases, this will @@ -115,7 +121,9 @@ class Layers(Module): except ValueError: pass - def _lm_pressed(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> None: + def _lm_pressed( + self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + ) -> None: ''' As MO(layer) but with mod active ''' @@ -124,7 +132,9 @@ class Layers(Module): keyboard.keys_pressed.add(key.meta.kc) self._mo_pressed(key, keyboard, *args, **kwargs) - def _lm_released(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> None: + def _lm_released( + self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + ) -> None: ''' As MO(layer) but with mod active ''' @@ -132,12 +142,16 @@ class Layers(Module): keyboard.keys_pressed.discard(key.meta.kc) self._mo_released(key, keyboard, *args, **kwargs) - def _lt_pressed(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> None: + def _lt_pressed( + self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + ) -> None: # Sets the timer start and acts like MO otherwise self.start_time[LayerType.LT] = accurate_ticks() self._mo_pressed(key, keyboard, *args, **kwargs) - def _lt_released(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> None: + def _lt_released( + self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + ) -> None: # On keyup, check timer, and press key if needed. if self.start_time[LayerType.LT] and ( accurate_ticks_diff( @@ -150,7 +164,9 @@ class Layers(Module): self._mo_released(key, keyboard, *args, **kwargs) self.start_time[LayerType.LT] = None - def _tg_pressed(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> None: + def _tg_pressed( + self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + ) -> None: ''' Toggles the layer (enables it if not active, and vise versa) ''' @@ -161,14 +177,18 @@ class Layers(Module): except ValueError: keyboard.active_layers.insert(0, key.meta.layer) - def _to_pressed(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> None: + def _to_pressed( + self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + ) -> None: ''' Activates layer and deactivates all other layers ''' keyboard.active_layers.clear() keyboard.active_layers.insert(0, key.meta.layer) - def _tt_pressed(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> None: + def _tt_pressed( + self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + ) -> None: ''' Momentarily activates layer if held, toggles it if tapped repeatedly ''' @@ -184,7 +204,9 @@ class Layers(Module): return return - def _tt_released(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> None: + def _tt_released( + self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + ) -> None: if self.start_time[LayerType.TT] is None or not accurate_ticks_diff( accurate_ticks(), self.start_time[LayerType.TT], keyboard.tap_time ): diff --git a/kmk/modules/modtap.py b/kmk/modules/modtap.py index b92baeb..39b452d 100644 --- a/kmk/modules/modtap.py +++ b/kmk/modules/modtap.py @@ -38,14 +38,18 @@ class ModTap(Module): def on_powersave_disable(self, keyboard: KMKKeyboard) -> None: return - def mt_pressed(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> KMKKeyboard: + def mt_pressed( + self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + ) -> KMKKeyboard: '''Sets the timer start and acts like a modifier otherwise''' keyboard.keys_pressed.add(key.meta.mods) self._mod_tap_timer = accurate_ticks() return keyboard - def mt_released(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> KMKKeyboard: + def mt_released( + self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + ) -> KMKKeyboard: '''On keyup, check timer, and press key if needed.''' keyboard.keys_pressed.discard(key.meta.mods) if self._mod_tap_timer and ( diff --git a/kmk/modules/power.py b/kmk/modules/power.py index 9b4703e..f4d2ad3 100644 --- a/kmk/modules/power.py +++ b/kmk/modules/power.py @@ -1,4 +1,3 @@ -from kmk.kmk_keyboard import KMKKeyboard import board import digitalio @@ -6,6 +5,7 @@ from typing import Any, Dict, Optional from kmk.handlers.stock import passthrough as handler_passthrough from kmk.keys import Key, make_key +from kmk.kmk_keyboard import KMKKeyboard from kmk.kmktime import sleep_ms, ticks_diff, ticks_ms from kmk.modules import Module @@ -134,16 +134,22 @@ class Power(Module): # https://github.com/adafruit/circuitpython/pull/3513 return True - def _ps_tog(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> None: + def _ps_tog( + self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + ) -> None: if self.enable: keyboard._trigger_powersave_disable = True else: keyboard._trigger_powersave_enable = True - def _ps_enable(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> None: + def _ps_enable( + self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + ) -> None: if not self.enable: keyboard._trigger_powersave_enable = True - def _ps_disable(self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any) -> None: + def _ps_disable( + self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + ) -> None: if self.enable: keyboard._trigger_powersave_disable = True diff --git a/kmk/types.py b/kmk/types.py index bd8e454..a1c370d 100644 --- a/kmk/types.py +++ b/kmk/types.py @@ -23,7 +23,9 @@ class LayerKeyMeta: class ModTapKeyMeta: - def __init__(self, kc: Optional[Key] = None, mods: Optional[List[Key]] = None) -> None: + def __init__( + self, kc: Optional[Key] = None, mods: Optional[List[Key]] = None + ) -> None: self.mods: Optional[List[Key]] = mods self.kc: Optional[Key] = kc From 908da846fe1a44c91083b09255bf7ccfa9b8575a Mon Sep 17 00:00:00 2001 From: sofubi Date: Mon, 16 Aug 2021 10:33:05 +0000 Subject: [PATCH 08/11] Fix make test errors --- kmk/key_validators.py | 2 +- kmk/kmk_keyboard.py | 2 +- kmk/modules/layers.py | 100 +++++++++++++++++++++++++++++++++++------- kmk/modules/modtap.py | 12 ++++- kmk/modules/power.py | 18 ++++++-- kmk/modules/split.py | 2 +- 6 files changed, 112 insertions(+), 24 deletions(-) diff --git a/kmk/key_validators.py b/kmk/key_validators.py index 6e3018a..651a8c8 100644 --- a/kmk/key_validators.py +++ b/kmk/key_validators.py @@ -1,4 +1,4 @@ -from typing import List, Optional, Tuple +from typing import List, Optional from kmk.keys import Key from kmk.types import ( diff --git a/kmk/kmk_keyboard.py b/kmk/kmk_keyboard.py index 231d084..d2af92d 100644 --- a/kmk/kmk_keyboard.py +++ b/kmk/kmk_keyboard.py @@ -441,7 +441,7 @@ class KMKKeyboard: self, hid_type: int = HIDModes.USB, secondary_hid_type: Optional[int] = None, - **kwargs: Dict[Any, Any] + **kwargs: Dict[Any, Any], ) -> None: self.hid_type = hid_type self.secondary_hid_type = secondary_hid_type diff --git a/kmk/modules/layers.py b/kmk/modules/layers.py index b00f8d6..00c30d7 100644 --- a/kmk/modules/layers.py +++ b/kmk/modules/layers.py @@ -88,7 +88,11 @@ class Layers(Module): return def _df_pressed( - self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + self, + key: Key, + keyboard: KMKKeyboard, + *args: Any, + **kwargs: Any, ) -> None: ''' Switches the default layer @@ -96,7 +100,11 @@ class Layers(Module): keyboard.active_layers[-1] = key.meta.layer def _mo_pressed( - self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + self, + key: Key, + keyboard: KMKKeyboard, + *args: Any, + **kwargs: Any, ) -> None: ''' Momentarily activates layer, switches off when you let go @@ -105,7 +113,10 @@ class Layers(Module): @staticmethod def _mo_released( - key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + key: Key, + keyboard: KMKKeyboard, + *args: Any, + **kwargs: Any, ) -> None: # remove the first instance of the target layer # from the active list @@ -122,7 +133,11 @@ class Layers(Module): pass def _lm_pressed( - self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + self, + key: Key, + keyboard: KMKKeyboard, + *args: Any, + **kwargs: Any, ) -> None: ''' As MO(layer) but with mod active @@ -130,10 +145,19 @@ class Layers(Module): keyboard.hid_pending = True # Sets the timer start and acts like MO otherwise keyboard.keys_pressed.add(key.meta.kc) - self._mo_pressed(key, keyboard, *args, **kwargs) + self._mo_pressed( + key, + keyboard, + *args, + **kwargs, + ) def _lm_released( - self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + self, + key: Key, + keyboard: KMKKeyboard, + *args: Any, + **kwargs: Any, ) -> None: ''' As MO(layer) but with mod active @@ -143,14 +167,22 @@ class Layers(Module): self._mo_released(key, keyboard, *args, **kwargs) def _lt_pressed( - self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + self, + key: Key, + keyboard: KMKKeyboard, + *args: Any, + **kwargs: Any, ) -> None: # Sets the timer start and acts like MO otherwise self.start_time[LayerType.LT] = accurate_ticks() self._mo_pressed(key, keyboard, *args, **kwargs) def _lt_released( - self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + self, + key: Key, + keyboard: KMKKeyboard, + *args: Any, + **kwargs: Any, ) -> None: # On keyup, check timer, and press key if needed. if self.start_time[LayerType.LT] and ( @@ -161,11 +193,20 @@ class Layers(Module): keyboard.hid_pending = True keyboard.tap_key(key.meta.kc) - self._mo_released(key, keyboard, *args, **kwargs) + self._mo_released( + key, + keyboard, + *args, + **kwargs, + ) self.start_time[LayerType.LT] = None def _tg_pressed( - self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + self, + key: Key, + keyboard: KMKKeyboard, + *args: Any, + **kwargs: Any, ) -> None: ''' Toggles the layer (enables it if not active, and vise versa) @@ -178,7 +219,11 @@ class Layers(Module): keyboard.active_layers.insert(0, key.meta.layer) def _to_pressed( - self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + self, + key: Key, + keyboard: KMKKeyboard, + *args: Any, + **kwargs: Any, ) -> None: ''' Activates layer and deactivates all other layers @@ -187,7 +232,11 @@ class Layers(Module): keyboard.active_layers.insert(0, key.meta.layer) def _tt_pressed( - self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + self, + key: Key, + keyboard: KMKKeyboard, + *args: Any, + **kwargs: Any, ) -> None: ''' Momentarily activates layer if held, toggles it if tapped repeatedly @@ -195,17 +244,31 @@ class Layers(Module): if self.start_time[LayerType.TT] is None: # Sets the timer start and acts like MO otherwise self.start_time[LayerType.TT] = accurate_ticks() - self._mo_pressed(key, keyboard, *args, **kwargs) + self._mo_pressed( + key, + keyboard, + *args, + **kwargs, + ) elif accurate_ticks_diff( accurate_ticks(), self.start_time[LayerType.TT], keyboard.tap_time ): self.start_time[LayerType.TT] = None - self._tg_pressed(key, keyboard, *args, **kwargs) + self._tg_pressed( + key, + keyboard, + *args, + **kwargs, + ) return return def _tt_released( - self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + self, + key: Key, + keyboard: KMKKeyboard, + *args: Any, + **kwargs: Any, ) -> None: if self.start_time[LayerType.TT] is None or not accurate_ticks_diff( accurate_ticks(), self.start_time[LayerType.TT], keyboard.tap_time @@ -213,4 +276,9 @@ class Layers(Module): # On first press, works like MO. On second press, does nothing unless let up within # time window, then acts like TG. self.start_time[LayerType.TT] = None - self._mo_released(key, keyboard, *args, **kwargs) + self._mo_released( + key, + keyboard, + *args, + **kwargs, + ) diff --git a/kmk/modules/modtap.py b/kmk/modules/modtap.py index 39b452d..16ffda7 100644 --- a/kmk/modules/modtap.py +++ b/kmk/modules/modtap.py @@ -39,7 +39,11 @@ class ModTap(Module): return def mt_pressed( - self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + self, + key: Key, + keyboard: KMKKeyboard, + *args: Any, + **kwargs: Any, ) -> KMKKeyboard: '''Sets the timer start and acts like a modifier otherwise''' keyboard.keys_pressed.add(key.meta.mods) @@ -48,7 +52,11 @@ class ModTap(Module): return keyboard def mt_released( - self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + self, + key: Key, + keyboard: KMKKeyboard, + *args: Any, + **kwargs: Any, ) -> KMKKeyboard: '''On keyup, check timer, and press key if needed.''' keyboard.keys_pressed.discard(key.meta.mods) diff --git a/kmk/modules/power.py b/kmk/modules/power.py index f4d2ad3..86cd0c1 100644 --- a/kmk/modules/power.py +++ b/kmk/modules/power.py @@ -135,7 +135,11 @@ class Power(Module): return True def _ps_tog( - self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + self, + key: Key, + keyboard: KMKKeyboard, + *args: Any, + **kwargs: Any, ) -> None: if self.enable: keyboard._trigger_powersave_disable = True @@ -143,13 +147,21 @@ class Power(Module): keyboard._trigger_powersave_enable = True def _ps_enable( - self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + self, + key: Key, + keyboard: KMKKeyboard, + *args: Any, + **kwargs: Any, ) -> None: if not self.enable: keyboard._trigger_powersave_enable = True def _ps_disable( - self, key: Key, keyboard: KMKKeyboard, *args: Any, **kwargs: Any + self, + key: Key, + keyboard: KMKKeyboard, + *args: Any, + **kwargs: Any, ) -> None: if self.enable: keyboard._trigger_powersave_disable = True diff --git a/kmk/modules/split.py b/kmk/modules/split.py index 6d30e7d..6cfb52b 100644 --- a/kmk/modules/split.py +++ b/kmk/modules/split.py @@ -3,7 +3,7 @@ import busio from micropython import const from storage import getmount -from typing import Any, List, Optional, Tuple, Type, Union +from typing import Any, List, Optional, Type, Union from kmk.kmk_keyboard import KMKKeyboard from kmk.kmktime import ticks_diff, ticks_ms From 36ea0eec4e8b06f8f671b971e7cf96e3bb74983f Mon Sep 17 00:00:00 2001 From: sofubi Date: Wed, 18 Aug 2021 20:39:18 -0400 Subject: [PATCH 09/11] Works towars typing keys.py --- kmk/handlers/stock.py | 21 +++++- kmk/keys.py | 153 +++++++++++++++++++++++++++++------------- 2 files changed, 124 insertions(+), 50 deletions(-) diff --git a/kmk/handlers/stock.py b/kmk/handlers/stock.py index 73069b8..2f942f3 100644 --- a/kmk/handlers/stock.py +++ b/kmk/handlers/stock.py @@ -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) diff --git a/kmk/keys.py b/kmk/keys.py index 1a0a4a3..b9db9ce 100644 --- a/kmk/keys.py +++ b/kmk/keys.py @@ -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. From 9fc431e0a7416f56f402f4ba78798c0f7bc6eae1 Mon Sep 17 00:00:00 2001 From: sofubi Date: Wed, 18 Aug 2021 21:47:17 -0400 Subject: [PATCH 10/11] Final first pass of keys.py --- kmk/handlers/stock.py | 7 ++++--- kmk/keys.py | 48 ++++++++++++++++++++++++++++++------------- 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/kmk/handlers/stock.py b/kmk/handlers/stock.py index 2f942f3..515e5ba 100644 --- a/kmk/handlers/stock.py +++ b/kmk/handlers/stock.py @@ -1,5 +1,6 @@ -from kmk.keys import KeyAttrDict from typing import Any, Optional + +from kmk.keys import Key, KeyAttrDict from kmk.kmk_keyboard import KMKKeyboard from kmk.kmktime import sleep_ms @@ -9,7 +10,7 @@ def passthrough(key, keyboard, *args, **kwargs): def default_pressed( - key: str, + key: Key, keyboard: KMKKeyboard, KC: KeyAttrDict, coord_int: Optional[int] = None, @@ -28,7 +29,7 @@ def default_pressed( def default_released( - key: str, + key: Key, keyboard: KMKKeyboard, KC: KeyAttrDict, coord_int: Optional[int] = None, diff --git a/kmk/keys.py b/kmk/keys.py index b9db9ce..92e5f3e 100644 --- a/kmk/keys.py +++ b/kmk/keys.py @@ -1,9 +1,8 @@ -import gc -from kmk.kmk_keyboard import KMKKeyboard -from micropython import const - from __future__ import annotations +import gc +from micropython import const + from typing import Any, Callable, List, Optional, Protocol, Set, Tuple, Union import kmk.handlers.stock as handlers @@ -13,6 +12,7 @@ from kmk.key_validators import ( tap_dance_key_validator, unicode_mode_key_validator, ) +from kmk.kmk_keyboard import KMKKeyboard from kmk.types import AttrDict, UnicodeModeKeyMeta DEBUG_OUTPUT: bool = False @@ -92,7 +92,7 @@ def maybe_make_consumer_key( class KeyAttrDict(AttrDict): - def __getattr__(self, key: str, depth: int = 0) -> str: + def __getattr__(self, key: str, depth: int = 0) -> KeyReturn: if depth > 1: raise InfiniteLoopDetected() @@ -686,7 +686,12 @@ 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): # NOQA +def make_key( + code: Optional[int] = None, + names: Tuple[str, ...] = tuple(), + type: int = KEY_SIMPLE, + **kwargs: Any, +) -> KeyReturn: # NOQA ''' Create a new key, aliased by `names` in the KC lookup table. @@ -728,7 +733,12 @@ def make_key(code: Optional[int] = None, names Tuple[str, ...] = tuple(), type: return key -def make_mod_key(code, names, *args, **kwargs): +def make_mod_key( + code: Optional[int], + names: Tuple[str, ...], + *args: Any, + **kwargs: Any, +): return make_key(code, names, *args, **kwargs, type=KEY_MODIFIER) @@ -747,21 +757,31 @@ def make_shifted_key(target_name, names=tuple()): # NOQA return key -def make_consumer_key(*args, **kwargs): +def make_consumer_key(*args: Any, **kwargs: Any): 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=lambda *validator_args, **validator_kwargs: object(), - names=tuple(), # NOQA - *constructor_args, - **constructor_kwargs, -): + validator: Validator = lambda *validator_args, **validator_kwargs: object(), + names: Tuple = tuple(), # NOQA + *constructor_args: Any, + **constructor_kwargs: Any, +) -> Optional[ArgumentedKey]: global NEXT_AVAILABLE_KEY - def _argumented_key(*user_args, **user_kwargs): + def _argumented_key(*user_args: Any, **user_kwargs: Any): global NEXT_AVAILABLE_KEY meta = validator(*user_args, **user_kwargs) From b202dc77d14941190d5c3e55763f79028aa70243 Mon Sep 17 00:00:00 2001 From: sofubi Date: Fri, 27 Aug 2021 00:33:28 -0400 Subject: [PATCH 11/11] Reformat type hints to use comment style syntax Update Pipfile to add typing module and pyright Update pyproject.toml for pyright and mypy configs --- Pipfile | 2 + Pipfile.lock | 131 ++++++++++------- kmk/consts.py | 14 ++ kmk/extensions/__init__.py | 16 +- kmk/extensions/international.py | 10 ++ kmk/handlers/stock.py | 43 +++--- kmk/key_validators.py | 25 +++- kmk/keys.py | 253 +++++++++++++++----------------- kmk/kmk_keyboard.py | 183 +++++++++++++---------- kmk/kmktime.py | 15 +- kmk/types.py | 49 ++++--- pyproject.toml | 9 +- 12 files changed, 439 insertions(+), 311 deletions(-) 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"