diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 08442b6..d2a462e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,7 +48,7 @@ jobs: - 'Dockerfile' - '.github/workflows/ci.yml' - lint: + format_and_lint: timeout-minutes: 3 runs-on: ubuntu-latest @@ -95,7 +95,7 @@ jobs: - name: Install dependencies run: | source .venv/bin/activate - poetry install --no-root --with format + poetry install --no-root --with format,lint - name: Check for successful installation run: | @@ -106,3 +106,8 @@ jobs: run: | source .venv/bin/activate poetry run python tasks/format.py --dry-run + + - name: Format and lint project + run: | + source .venv/bin/activate + poetry run mypy diff --git a/README.md b/README.md index e3b4f7d..b555560 100644 --- a/README.md +++ b/README.md @@ -129,3 +129,9 @@ poetry run pytest -rfsxE --capture=no --log-cli-level=DEBUG --maxfail=1 -vv ./te ```bash poetry run python ./tasks/format.py ``` + +#### Code Linting + +```bash +poetry run mypy +``` diff --git a/poetry.lock b/poetry.lock index 15599cd..f8aaf15 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.3.4 and should not be changed by hand. [[package]] name = "colorama" @@ -56,6 +56,107 @@ files = [ {file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"}, ] +[[package]] +name = "librt" +version = "0.9.0" +description = "Mypyc runtime library" +optional = false +python-versions = ">=3.9" +groups = ["lint"] +markers = "platform_python_implementation != \"PyPy\"" +files = [ + {file = "librt-0.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f8e12706dcb8ff6b3ed57514a19e45c49ad00bcd423e87b2b2e4b5f64578443"}, + {file = "librt-0.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4e3dda8345307fd7306db0ed0cb109a63a2c85ba780eb9dc2d09b2049a931f9c"}, + {file = "librt-0.9.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:de7dac64e3eb832ffc7b840eb8f52f76420cde1b845be51b2a0f6b870890645e"}, + {file = "librt-0.9.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22a904cbdb678f7cb348c90d543d3c52f581663d687992fee47fd566dcbf5285"}, + {file = "librt-0.9.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:224b9727eb8bc188bc3bcf29d969dba0cd61b01d9bac80c41575520cc4baabb2"}, + {file = "librt-0.9.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e94cbc6ad9a6aeea46d775cbb11f361022f778a9cc8cc90af653d3a594b057ce"}, + {file = "librt-0.9.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7bc30ad339f4e1a01d4917d645e522a0bc0030644d8973f6346397c93ba1503f"}, + {file = "librt-0.9.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:56d65b583cf43b8cf4c8fbe1e1da20fa3076cc32a1149a141507af1062718236"}, + {file = "librt-0.9.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0a1be03168b2691ba61927e299b352a6315189199ca18a57b733f86cb3cc8d38"}, + {file = "librt-0.9.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:63c12efcd160e1d14da11af0c46c0217473e1e0d2ae1acbccc83f561ea4c2a7b"}, + {file = "librt-0.9.0-cp310-cp310-win32.whl", hash = "sha256:e9002e98dcb1c0a66723592520decd86238ddcef168b37ff6cfb559200b4b774"}, + {file = "librt-0.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:9fcb461fbf70654a52a7cc670e606f04449e2374c199b1825f754e16dacfedd8"}, + {file = "librt-0.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:90904fac73c478f4b83f4ed96c99c8208b75e6f9a8a1910548f69a00f1eaa671"}, + {file = "librt-0.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:789fff71757facc0738e8d89e3b84e4f0251c1c975e85e81b152cdaca927cc2d"}, + {file = "librt-0.9.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1bf465d1e5b0a27713862441f6467b5ab76385f4ecf8f1f3a44f8aa3c695b4b6"}, + {file = "librt-0.9.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f819e0c6413e259a17a7c0d49f97f405abadd3c2a316a3b46c6440b7dbbedbb1"}, + {file = "librt-0.9.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e0785c2fb4a81e1aece366aa3e2e039f4a4d7d21aaaded5227d7f3c703427882"}, + {file = "librt-0.9.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:80b25c7b570a86c03b5da69e665809deb39265476e8e21d96a9328f9762f9990"}, + {file = "librt-0.9.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d4d16b608a1c43d7e33142099a75cd93af482dadce0bf82421e91cad077157f4"}, + {file = "librt-0.9.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:194fc1a32e1e21fe809d38b5faea66cc65eaa00217c8901fbdb99866938adbdb"}, + {file = "librt-0.9.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:8c6bc1384d9738781cfd41d09ad7f6e8af13cfea2c75ece6bd6d2566cdea2076"}, + {file = "librt-0.9.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:15cb151e52a044f06e54ac7f7b47adbfc89b5c8e2b63e1175a9d587c43e8942a"}, + {file = "librt-0.9.0-cp311-cp311-win32.whl", hash = "sha256:f100bfe2acf8a3689af9d0cc660d89f17286c9c795f9f18f7b62dd1a6b247ae6"}, + {file = "librt-0.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:0b73e4266307e51c95e09c0750b7ec383c561d2e97d58e473f6f6a209952fbb8"}, + {file = "librt-0.9.0-cp311-cp311-win_arm64.whl", hash = "sha256:bc5518873822d2faa8ebdd2c1a4d7c8ef47b01a058495ab7924cb65bdbf5fc9a"}, + {file = "librt-0.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9b3e3bc363f71bda1639a4ee593cb78f7fbfeacc73411ec0d4c92f00730010a4"}, + {file = "librt-0.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0a09c2f5869649101738653a9b7ab70cf045a1105ac66cbb8f4055e61df78f2d"}, + {file = "librt-0.9.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5ca8e133d799c948db2ab1afc081c333a825b5540475164726dcbf73537e5c2f"}, + {file = "librt-0.9.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:603138ee838ee1583f1b960b62d5d0007845c5c423feb68e44648b1359014e27"}, + {file = "librt-0.9.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4003f70c56a5addd6aa0897f200dd59afd3bf7bcd5b3cce46dd21f925743bc2"}, + {file = "librt-0.9.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:78042f6facfd98ecb25e9829c7e37cce23363d9d7c83bc5f72702c5059eb082b"}, + {file = "librt-0.9.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a361c9434a64d70a7dbb771d1de302c0cc9f13c0bffe1cf7e642152814b35265"}, + {file = "librt-0.9.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:dd2c7e082b0b92e1baa4da28163a808672485617bc855cc22a2fd06978fa9084"}, + {file = "librt-0.9.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:7e6274fd33fc5b2a14d41c9119629d3ff395849d8bcbc80cf637d9e8d2034da8"}, + {file = "librt-0.9.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5093043afb226ecfa1400120d1ebd4442b4f99977783e4f4f7248879009b227f"}, + {file = "librt-0.9.0-cp312-cp312-win32.whl", hash = "sha256:9edcc35d1cae9fd5320171b1a838c7da8a5c968af31e82ecc3dff30b4be0957f"}, + {file = "librt-0.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:3cc2917258e131ae5f958a4d872e07555b51cb7466a43433218061c74ef33745"}, + {file = "librt-0.9.0-cp312-cp312-win_arm64.whl", hash = "sha256:90e6d5420fc8a300518d4d2288154ff45005e920425c22cbbfe8330f3f754bd9"}, + {file = "librt-0.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f29b68cd9714531672db62cc54f6e8ff981900f824d13fa0e00749189e13778e"}, + {file = "librt-0.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7d5c8a5929ac325729f6119802070b561f4db793dffc45e9ac750992a4ed4d22"}, + {file = "librt-0.9.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:756775d25ec8345b837ab52effee3ad2f3b2dfd6bbee3e3f029c517bd5d8f05a"}, + {file = "librt-0.9.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b8f5d00b49818f4e2b1667db994488b045835e0ac16fe2f924f3871bd2b8ac5"}, + {file = "librt-0.9.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c81aef782380f0f13ead670aae01825eb653b44b046aa0e5ebbb79f76ed4aa11"}, + {file = "librt-0.9.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:66b58fed90a545328e80d575467244de3741e088c1af928f0b489ebec3ef3858"}, + {file = "librt-0.9.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e78fb7419e07d98c2af4b8567b72b3eaf8cb05caad642e9963465569c8b2d87e"}, + {file = "librt-0.9.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c3786f0f4490a5cd87f1ed6cefae833ad6b1060d52044ce0434a2e85893afd0"}, + {file = "librt-0.9.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8494cfc61e03542f2d381e71804990b3931175a29b9278fdb4a5459948778dc2"}, + {file = "librt-0.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:07cf11f769831186eeac424376e6189f20ace4f7263e2134bdb9757340d84d4d"}, + {file = "librt-0.9.0-cp313-cp313-win32.whl", hash = "sha256:850d6d03177e52700af605fd60db7f37dcb89782049a149674d1a9649c2138fd"}, + {file = "librt-0.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:a5af136bfba820d592f86c67affcef9b3ff4d4360ac3255e341e964489b48519"}, + {file = "librt-0.9.0-cp313-cp313-win_arm64.whl", hash = "sha256:4c4d0440a3a8e31d962340c3e1cc3fc9ee7febd34c8d8f770d06adb947779ea5"}, + {file = "librt-0.9.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:3f05d145df35dca5056a8bc3838e940efebd893a54b3e19b2dda39ceaa299bcb"}, + {file = "librt-0.9.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1c587494461ebd42229d0f1739f3aa34237dd9980623ecf1be8d3bcba79f4499"}, + {file = "librt-0.9.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b0a2040f801406b93657a70b72fa12311063a319fee72ce98e1524da7200171f"}, + {file = "librt-0.9.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f38bc489037eca88d6ebefc9c4d41a4e07c8e8b4de5188a9e6d290273ad7ebb1"}, + {file = "librt-0.9.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3fd278f5e6bf7c75ccd6d12344eb686cc020712683363b66f46ac79d37c799f"}, + {file = "librt-0.9.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fcbdf2a9ca24e87bbebb47f1fe34e531ef06f104f98c9ccfc953a3f3344c567a"}, + {file = "librt-0.9.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e306d956cfa027fe041585f02a1602c32bfa6bb8ebea4899d373383295a6c62f"}, + {file = "librt-0.9.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:465814ab157986acb9dfa5ccd7df944be5eefc0d08d31ec6e8d88bc71251d845"}, + {file = "librt-0.9.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:703f4ae36d6240bfe24f542bac784c7e4194ec49c3ba5a994d02891649e2d85b"}, + {file = "librt-0.9.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3be322a15ee5e70b93b7a59cfd074614f22cc8c9ff18bd27f474e79137ea8d3b"}, + {file = "librt-0.9.0-cp314-cp314-win32.whl", hash = "sha256:b8da9f8035bb417770b1e1610526d87ad4fc58a2804dc4d79c53f6d2cf5a6eb9"}, + {file = "librt-0.9.0-cp314-cp314-win_amd64.whl", hash = "sha256:b8bd70d5d816566a580d193326912f4a76ec2d28a97dc4cd4cc831c0af8e330e"}, + {file = "librt-0.9.0-cp314-cp314-win_arm64.whl", hash = "sha256:fc5758e2b7a56532dc33e3c544d78cbaa9ecf0a0f2a2da2df882c1d6b99a317f"}, + {file = "librt-0.9.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:f24b90b0e0c8cc9491fb1693ae91fe17cb7963153a1946395acdbdd5818429a4"}, + {file = "librt-0.9.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3fe56e80badb66fdcde06bef81bbaa5bfcf6fbd7aefb86222d9e369c38c6b228"}, + {file = "librt-0.9.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:527b5b820b47a09e09829051452bb0d1dd2122261254e2a6f674d12f1d793d54"}, + {file = "librt-0.9.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d429bdd4ac0ab17c8e4a8af0ed2a7440b16eba474909ab357131018fe8c7e71"}, + {file = "librt-0.9.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7202bdcac47d3a708271c4304a474a8605a4a9a4a709e954bf2d3241140aa938"}, + {file = "librt-0.9.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0d620e74897f8c2613b3c4e2e9c1e422eb46d2ddd07df540784d44117836af3"}, + {file = "librt-0.9.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d69fc39e627908f4c03297d5a88d9284b73f4d90b424461e32e8c2485e21c283"}, + {file = "librt-0.9.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:c2640e23d2b7c98796f123ffd95cf2022c7777aa8a4a3b98b36c570d37e85eee"}, + {file = "librt-0.9.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:451daa98463b7695b0a30aa56bf637831ea559e7b8101ac2ef6382e8eb15e29c"}, + {file = "librt-0.9.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:928bd06eca2c2bbf4349e5b817f837509b0604342e65a502de1d50a7570afd15"}, + {file = "librt-0.9.0-cp314-cp314t-win32.whl", hash = "sha256:a9c63e04d003bc0fb6a03b348018b9a3002f98268200e22cc80f146beac5dc40"}, + {file = "librt-0.9.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f162af66a2ed3f7d1d161a82ca584efd15acd9c1cff190a373458c32f7d42118"}, + {file = "librt-0.9.0-cp314-cp314t-win_arm64.whl", hash = "sha256:a4b25c6c25cac5d0d9d6d6da855195b254e0021e513e0249f0e3b444dc6e0e61"}, + {file = "librt-0.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5112c2fb7c2eefefaeaf5c97fec81343ef44ee86a30dcfaa8223822fba6467b4"}, + {file = "librt-0.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a81eea9b999b985e4bacc650c4312805ea7008fd5e45e1bf221310176a7bcb3a"}, + {file = "librt-0.9.0-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:eea1b54943475f51698f85fa230c65ccac769f1e603b981be060ac5763d90927"}, + {file = "librt-0.9.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:81107843ed1836874b46b310f9b1816abcb89912af627868522461c3b7333c0f"}, + {file = "librt-0.9.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aa95738a68cedd3a6f5492feddc513e2e166b50602958139e47bbdd82da0f5a7"}, + {file = "librt-0.9.0-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6788207daa0c19955d2b668f3294a368d19f67d9b5f274553fd073c1260cbb9f"}, + {file = "librt-0.9.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f48c963a76d71b9d7927eb817b543d0dccd52ab6648b99d37bd54f4cd475d856"}, + {file = "librt-0.9.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:42ff8a962554c350d4a83cf47d9b7b78b0e6ff7943e87df7cdfc97c07f3c016f"}, + {file = "librt-0.9.0-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:657f8ba7b9eaaa82759a104137aed2a3ef7bc46ccfd43e0d89b04005b3e0a4cc"}, + {file = "librt-0.9.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2d03fa4fd277a7974c1978c92c374c57f44edeee163d147b477b143446ad1bf6"}, + {file = "librt-0.9.0-cp39-cp39-win32.whl", hash = "sha256:d9da80e5b04acce03ced8ba6479a71c2a2edf535c2acc0d09c80d2f80f3bad15"}, + {file = "librt-0.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:54d412e47c21b85865676ed0724e37a89e9593c2eee1e7367adf85bfad56ffb1"}, + {file = "librt-0.9.0.tar.gz", hash = "sha256:a0951822531e7aee6e0dfb556b30d5ee36bbe234faf60c20a16c01be3530869d"}, +] + [[package]] name = "mss" version = "10.1.0" @@ -73,6 +174,86 @@ dev = ["build (==1.3.0)", "mypy (==1.17.1)", "ruff (==0.12.9)", "twine (==6.1.0) docs = ["shibuya (==2025.7.24)", "sphinx (==8.2.3)", "sphinx-copybutton (==0.5.2)", "sphinx-new-tab-link (==0.8.0)"] tests = ["numpy (==2.2.4) ; sys_platform == \"linux\" and python_version == \"3.13\"", "pillow (==11.3.0) ; sys_platform == \"linux\" and python_version == \"3.13\"", "pytest (==8.4.1)", "pytest-cov (==6.2.1)", "pytest-rerunfailures (==15.1)", "pyvirtualdisplay (==3.0) ; sys_platform == \"linux\""] +[[package]] +name = "mypy" +version = "1.20.1" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.10" +groups = ["lint"] +files = [ + {file = "mypy-1.20.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3ba5d1e712ada9c3b6223dcbc5a31dac334ed62991e5caa17bcf5a4ddc349af0"}, + {file = "mypy-1.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e731284c117b0987fb1e6c5013a56f33e7faa1fce594066ab83876183ce1c66"}, + {file = "mypy-1.20.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f8e945b872a05f4fbefabe2249c0b07b6b194e5e11a86ebee9edf855de09806c"}, + {file = "mypy-1.20.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fc88acef0dc9b15246502b418980478c1bfc9702057a0e1e7598d01a7af8937"}, + {file = "mypy-1.20.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:14911a115c73608f155f648b978c5055d16ff974e6b1b5512d7fedf4fa8b15c6"}, + {file = "mypy-1.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:76d9b4c992cca3331d9793ef197ae360ea44953cf35beb2526e95b9e074f2866"}, + {file = "mypy-1.20.1-cp310-cp310-win_arm64.whl", hash = "sha256:b408722f80be44845da555671a5ef3a0c63f51ca5752b0c20e992dc9c0fbd3cd"}, + {file = "mypy-1.20.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c01eb9bac2c6a962d00f9d23421cd2913840e65bba365167d057bd0b4171a92e"}, + {file = "mypy-1.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:55d12ddbd8a9cac5b276878bd534fa39fff5bf543dc6ae18f25d30c8d7d27fca"}, + {file = "mypy-1.20.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0aa322c1468b6cdfc927a44ce130f79bb44bcd34eb4a009eb9f96571fd80955"}, + {file = "mypy-1.20.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3f8bc95899cf676b6e2285779a08a998cc3a7b26f1026752df9d2741df3c79e8"}, + {file = "mypy-1.20.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:47c2b90191a870a04041e910277494b0d92f0711be9e524d45c074fe60c00b65"}, + {file = "mypy-1.20.1-cp311-cp311-win_amd64.whl", hash = "sha256:9857dc8d2ec1a392ffbda518075beb00ac58859979c79f9e6bdcb7277082c2f2"}, + {file = "mypy-1.20.1-cp311-cp311-win_arm64.whl", hash = "sha256:09d8df92bb25b6065ab91b178da843dda67b33eb819321679a6e98a907ce0e10"}, + {file = "mypy-1.20.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:36ee2b9c6599c230fea89bbd79f401f9f9f8e9fcf0c777827789b19b7da90f51"}, + {file = "mypy-1.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fba3fb0968a7b48806b0c90f38d39296f10766885a94c83bd21399de1e14eb28"}, + {file = "mypy-1.20.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef1415a637cd3627d6304dfbeddbadd21079dafc2a8a753c477ce4fc0c2af54f"}, + {file = "mypy-1.20.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef3461b1ad5cd446e540016e90b5984657edda39f982f4cc45ca317b628f5a37"}, + {file = "mypy-1.20.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:542dd63c9e1339b6092eb25bd515f3a32a1453aee8c9521d2ddb17dacd840237"}, + {file = "mypy-1.20.1-cp312-cp312-win_amd64.whl", hash = "sha256:1d55c7cd8ca22e31f93af2a01160a9e95465b5878de23dba7e48116052f20a8d"}, + {file = "mypy-1.20.1-cp312-cp312-win_arm64.whl", hash = "sha256:f5b84a79070586e0d353ee07b719d9d0a4aa7c8ee90c0ea97747e98cbe193019"}, + {file = "mypy-1.20.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f3886c03e40afefd327bd70b3f634b39ea82e87f314edaa4d0cce4b927ddcc1"}, + {file = "mypy-1.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e860eb3904f9764e83bafd70c8250bdffdc7dde6b82f486e8156348bf7ceb184"}, + {file = "mypy-1.20.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4b5aac6e785719da51a84f5d09e9e843d473170a9045b1ea7ea1af86225df4b"}, + {file = "mypy-1.20.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f37b6cd0fe2ad3a20f05ace48ca3523fc52ff86940e34937b439613b6854472e"}, + {file = "mypy-1.20.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4bbb0f6b54ce7cc350ef4a770650d15fa70edd99ad5267e227133eda9c94218"}, + {file = "mypy-1.20.1-cp313-cp313-win_amd64.whl", hash = "sha256:c3dc20f8ec76eecd77148cdd2f1542ed496e51e185713bf488a414f862deb8f2"}, + {file = "mypy-1.20.1-cp313-cp313-win_arm64.whl", hash = "sha256:a9d62bbac5d6d46718e2b0330b25e6264463ed832722b8f7d4440ff1be3ca895"}, + {file = "mypy-1.20.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:12927b9c0ed794daedcf1dab055b6c613d9d5659ac511e8d936d96f19c087d12"}, + {file = "mypy-1.20.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:752507dd481e958b2c08fc966d3806c962af5a9433b5bf8f3bdd7175c20e34fe"}, + {file = "mypy-1.20.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c614655b5a065e56274c6cbbe405f7cf7e96c0654db7ba39bc680238837f7b08"}, + {file = "mypy-1.20.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2c3f6221a76f34d5100c6d35b3ef6b947054123c3f8d6938a4ba00b1308aa572"}, + {file = "mypy-1.20.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4bdfc06303ac06500af71ea0cdbe995c502b3c9ba32f3f8313523c137a25d1b6"}, + {file = "mypy-1.20.1-cp314-cp314-win_amd64.whl", hash = "sha256:0131edd7eba289973d1ba1003d1a37c426b85cdef76650cd02da6420898a5eb3"}, + {file = "mypy-1.20.1-cp314-cp314-win_arm64.whl", hash = "sha256:33f02904feb2c07e1fdf7909026206396c9deeb9e6f34d466b4cfedb0aadbbe4"}, + {file = "mypy-1.20.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:168472149dd8cc505c98cefd21ad77e4257ed6022cd5ed2fe2999bed56977a5a"}, + {file = "mypy-1.20.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:eb674600309a8f22790cca883a97c90299f948183ebb210fbef6bcee07cb1986"}, + {file = "mypy-1.20.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef2b2e4cc464ba9795459f2586923abd58a0055487cbe558cb538ea6e6bc142a"}, + {file = "mypy-1.20.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dee461d396dd46b3f0ed5a098dbc9b8860c81c46ad44fa071afcfbc149f167c9"}, + {file = "mypy-1.20.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e364926308b3e66f1361f81a566fc1b2f8cd47fc8525e8136d4058a65a4b4f02"}, + {file = "mypy-1.20.1-cp314-cp314t-win_amd64.whl", hash = "sha256:a0c17fbd746d38c70cbc42647cfd884f845a9708a4b160a8b4f7e70d41f4d7fa"}, + {file = "mypy-1.20.1-cp314-cp314t-win_arm64.whl", hash = "sha256:db2cb89654626a912efda69c0d5c1d22d948265e2069010d3dde3abf751c7d08"}, + {file = "mypy-1.20.1-py3-none-any.whl", hash = "sha256:1aae28507f253fe82d883790d1c0a0d35798a810117c88184097fe8881052f06"}, + {file = "mypy-1.20.1.tar.gz", hash = "sha256:6fc3f4ecd52de81648fed1945498bf42fa2993ddfad67c9056df36ae5757f804"}, +] + +[package.dependencies] +librt = {version = ">=0.8.0", markers = "platform_python_implementation != \"PyPy\""} +mypy_extensions = ">=1.0.0" +pathspec = ">=1.0.0" +typing_extensions = ">=4.6.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +faster-cache = ["orjson"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +native-parser = ["ast-serialize (>=0.1.1,<1.0.0)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.8" +groups = ["lint"] +files = [ + {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, + {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, +] + [[package]] name = "numpy" version = "2.4.4" @@ -188,6 +369,24 @@ files = [ {file = "packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4"}, ] +[[package]] +name = "pathspec" +version = "1.0.4" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.9" +groups = ["lint"] +files = [ + {file = "pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723"}, + {file = "pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645"}, +] + +[package.extras] +hyperscan = ["hyperscan (>=0.7)"] +optional = ["typing-extensions (>=4)"] +re2 = ["google-re2 (>=1.1)"] +tests = ["pytest (>=9)", "typing-extensions (>=4.15)"] + [[package]] name = "pillow" version = "12.2.0" @@ -4107,13 +4306,37 @@ files = [ {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, ] +[[package]] +name = "types-psutil" +version = "7.2.2.20260408" +description = "Typing stubs for psutil" +optional = false +python-versions = ">=3.10" +groups = ["lint"] +files = [ + {file = "types_psutil-7.2.2.20260408-py3-none-any.whl", hash = "sha256:0c334f6f6bc9e9c24fca5c7d1f0b6971c961a0a2e3956dc5ce704722c01f9762"}, + {file = "types_psutil-7.2.2.20260408.tar.gz", hash = "sha256:e8053450685965b8cd52afb62569073d00ea9967ae78bb45dff5f606847f97f2"}, +] + +[[package]] +name = "types-pynput" +version = "1.8.1.20260408" +description = "Typing stubs for pynput" +optional = false +python-versions = ">=3.10" +groups = ["lint"] +files = [ + {file = "types_pynput-1.8.1.20260408-py3-none-any.whl", hash = "sha256:1c581052b88be85e45b71bd5e8cbcfdfb613a2b45d583af367da3f6a89745c36"}, + {file = "types_pynput-1.8.1.20260408.tar.gz", hash = "sha256:25ac9ab16c2cd23d9a8eea80dc46a1a733291fdaa0b6cbd24c21b0f28944b73e"}, +] + [[package]] name = "typing-extensions" version = "4.15.0" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["main", "lint"] files = [ {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, @@ -4122,4 +4345,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = ">=3.14" -content-hash = "450d3333d9c283a1657fa171877faf34cf92fefd556da033fa2f8e33ffefb9d5" +content-hash = "1d970670845a18862e13496d6edf5f48f498231ea4e6996cabf8e71699fb0e64" diff --git a/pyproject.toml b/pyproject.toml index 06cd489..675e622 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,6 +24,11 @@ test = [ format = [ "ruff (>=0.15.7,<0.16.0)" ] +lint = [ + "mypy (>=1.20.1,<2.0.0)", + "types-pynput (>=1.8.1.20260408,<2.0.0.0)", + "types-psutil (>=7.2.2.20260408,<8.0.0.0)" +] [tool.poetry] @@ -31,6 +36,13 @@ packages = [ { include = "visiongui", from = "src" } ] +[tool.mypy] +files = ["src"] +mypy_path = 'src' +explicit_package_bases = true +strict = true +exclude = '(\.venv|build|dist)' + [build-system] requires = ["poetry-core>=2.0.0,<3.0.0"] build-backend = "poetry.core.masonry.api" diff --git a/src/visiongui/__init__.py b/src/visiongui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/visiongui/driver/DesktopDriverInterface.py b/src/visiongui/driver/DesktopDriverInterface.py index f1c6d87..2312921 100644 --- a/src/visiongui/driver/DesktopDriverInterface.py +++ b/src/visiongui/driver/DesktopDriverInterface.py @@ -1,3 +1,4 @@ +import re import subprocess from abc import ABC, abstractmethod @@ -11,11 +12,11 @@ class DesktopDriverInterface(ABC): @property @abstractmethod - def process(self) -> subprocess.Popen | None: ... + def process(self) -> subprocess.Popen[bytes] | None: ... @process.setter @abstractmethod - def process(self, value: subprocess.Popen | None) -> None: ... + def process(self, value: subprocess.Popen[bytes] | None) -> None: ... @property @abstractmethod @@ -26,22 +27,22 @@ def window(self) -> pywinctl.Window | None: ... def window(self, value: pywinctl.Window | None) -> None: ... @abstractmethod - def launch_process(self, *, cmd: list[str]) -> subprocess.Popen: ... + def launch_process(self, *, cmd: list[str]) -> subprocess.Popen[bytes]: ... @abstractmethod def find_window( self, *, - title, - timeout: float, + title: re.Pattern[str], + timeout: int, ) -> pywinctl.Window: ... @abstractmethod def wait_for_window_to_disappear( self, *, - title, - timeout: float, + title: re.Pattern[str], + timeout: int, ) -> None: ... @abstractmethod @@ -59,10 +60,10 @@ def find_element_by_image( self, *, image_path: str, - timeout: float, + timeout: int, log_image_name: str, - margin_of_error: float, - time_held_stable_on_screen: float, + margin_of_error: int, + time_held_stable_on_screen: int, debug_output_base_path: str, match_with_color: bool = False, ) -> DesktopElementInterface: ... diff --git a/src/visiongui/driver/DesktopDriverWindowsImplementation.py b/src/visiongui/driver/DesktopDriverWindowsImplementation.py index 72559e6..652abc7 100644 --- a/src/visiongui/driver/DesktopDriverWindowsImplementation.py +++ b/src/visiongui/driver/DesktopDriverWindowsImplementation.py @@ -1,3 +1,4 @@ +import re import subprocess import pywinctl @@ -19,16 +20,16 @@ class DesktopDriverWindowsImplementation(DesktopDriverInterface): - def __init__(self): - self._process: subprocess.Popen | None = None + def __init__(self) -> None: + self._process: subprocess.Popen[bytes] | None = None self._window: pywinctl.Window | None = None @property - def process(self) -> subprocess.Popen | None: + def process(self) -> subprocess.Popen[bytes] | None: return self._process @process.setter - def process(self, value: subprocess.Popen | None) -> None: + def process(self, value: subprocess.Popen[bytes] | None) -> None: self._process = value @property @@ -39,15 +40,15 @@ def window(self) -> pywinctl.Window | None: def window(self, value: pywinctl.Window | None) -> None: self._window = value - def launch_process(self, *, cmd: list[str]) -> subprocess.Popen: + def launch_process(self, *, cmd: list[str]) -> subprocess.Popen[bytes]: self.process = launch_process(cmd=cmd) return self.process def find_window( self, *, - title, - timeout: float, + title: re.Pattern[str], + timeout: int, ) -> pywinctl.Window: return find_window( title=title, @@ -57,8 +58,8 @@ def find_window( def wait_for_window_to_disappear( self, *, - title, - timeout: float, + title: re.Pattern[str], + timeout: int, ) -> None: return wait_for_window_to_disappear( title=title, @@ -69,10 +70,10 @@ def find_element_by_image( self, *, image_path: str, - timeout: float, + timeout: int, log_image_name: str, - margin_of_error: float, - time_held_stable_on_screen: float, + margin_of_error: int, + time_held_stable_on_screen: int, debug_output_base_path: str, match_with_color: bool = False, ) -> DesktopElementInterface: diff --git a/src/visiongui/driver/close.py b/src/visiongui/driver/close.py index 52e37dc..b6d2d1f 100644 --- a/src/visiongui/driver/close.py +++ b/src/visiongui/driver/close.py @@ -16,7 +16,11 @@ def close( if not isinstance(driver, DesktopDriverInterface): raise TypeError("Expected driver to be an instance of DesktopDriver") - pid = driver.window.getPID() + window = driver.window + pid = None + if window is not None: + pid = window.getPID() + logger.debug(f"Forcefully killing process owning the window: {pid}") proc = psutil.Process(pid) proc.terminate() diff --git a/src/visiongui/driver/find_element_by_image.py b/src/visiongui/driver/find_element_by_image.py index 61674d6..2a47bc6 100644 --- a/src/visiongui/driver/find_element_by_image.py +++ b/src/visiongui/driver/find_element_by_image.py @@ -1,6 +1,7 @@ import logging import os import time +from typing import Callable import cv2 import numpy as np @@ -23,18 +24,22 @@ logger = logging.getLogger(__name__) -def _is_stable(get_current_location, timeout, time_held_stable_on_screen): +def _is_stable( + get_current_location: Callable[[], DesktopElementImplementation | bool], + timeout: int, + time_held_stable_on_screen: int, +) -> DesktopElementImplementation: start_time = time.time() - last_location = None + last_location: DesktopElementImplementation | None = None stable_start = None found_once = False while time.time() - start_time < timeout: current_location = get_current_location() - if current_location: + if isinstance(current_location, DesktopElementImplementation): found_once = True if ( - last_location + last_location is not None and current_location.top_left == last_location.top_left and current_location.bottom_right == last_location.bottom_right ): @@ -60,7 +65,13 @@ def _is_stable(get_current_location, timeout, time_held_stable_on_screen): raise ExceptionElementNotFound(timeout=timeout) -def _match_template(template, margin_of_error, monitor, screen_image, mask=None): +def _match_template( + template: np.ndarray, + margin_of_error: float, + monitor: dict[str, int], + screen_image: np.ndarray, + mask: np.ndarray | None = None, +) -> DesktopElementImplementation | bool: # Ensure screen image has same number of channels as template if len(template.shape) == 2: if len(screen_image.shape) == 3: @@ -109,11 +120,11 @@ def _match_template(template, margin_of_error, monitor, screen_image, mask=None) def find_element_by_image( image_path: str, - timeout: float, + timeout: int, log_image_name: str, debug_output_base_path: str, margin_of_error: float, - time_held_stable_on_screen: float, + time_held_stable_on_screen: int, match_with_color: bool = False, ) -> DesktopElementImplementation: if not image_path or not os.path.isfile(image_path): @@ -121,6 +132,9 @@ def find_element_by_image( # [68566fb0-e936-48e3-8c87-c5b8735567df] If the image has an alpha channel, extract it to build a binary mask. This mask ensures that only opaque regions of the template are matched, ignoring transparent padding. image = cv2.imread(image_path, cv2.IMREAD_UNCHANGED) + if image is None: + raise ValueError(f"Unable to read image at {image_path}") + mask = None template = None if len(image.shape) == 3 and image.shape[2] == 4: @@ -154,7 +168,7 @@ def find_element_by_image( with mss() as sct: monitor = sct.monitors[1] - def get_current_location(): + def get_current_location() -> DesktopElementImplementation | bool: screenshot = sct.grab(monitor) screen_image = np.array(screenshot) return _match_template( @@ -173,10 +187,12 @@ def get_current_location(): ) return result try: - result = WebDriverWait(timeout).until( + wait_result = WebDriverWait(timeout).until( condition=get_current_location, ) - return result + if not isinstance(wait_result, DesktopElementImplementation): + raise ExceptionElementNotFound(timeout=timeout) + return wait_result except ExceptionTimeout: save_debug_screenshot( image_file_name_prefix=FileNamePrefix.FAIL, diff --git a/src/visiongui/driver/find_window.py b/src/visiongui/driver/find_window.py index 1fde219..cf10cee 100644 --- a/src/visiongui/driver/find_window.py +++ b/src/visiongui/driver/find_window.py @@ -10,8 +10,8 @@ logger = logging.getLogger(__name__) -def _get_matching_window(title: re.Pattern) -> pywinctl.Window | None: - all_windows = pywinctl.getAllWindows() +def _get_matching_window(title: re.Pattern[str]) -> pywinctl.Window | None: + all_windows = pywinctl.getAllWindows() # type: ignore[no-untyped-call] titles = [window.title for window in all_windows if window.title.strip()] logger.debug(f"Checking all window titles: {titles}") @@ -25,8 +25,8 @@ def _get_matching_window(title: re.Pattern) -> pywinctl.Window | None: def find_window( - title: re.Pattern, - timeout: float, + title: re.Pattern[str], + timeout: int, ) -> pywinctl.Window: def _window_check() -> pywinctl.Window | None: return _get_matching_window(title) diff --git a/src/visiongui/driver/launch_process.py b/src/visiongui/driver/launch_process.py index c4b7c59..55e305f 100644 --- a/src/visiongui/driver/launch_process.py +++ b/src/visiongui/driver/launch_process.py @@ -2,7 +2,7 @@ import subprocess -def launch_process(cmd: list[str]) -> subprocess.Popen: +def launch_process(cmd: list[str]) -> subprocess.Popen[bytes]: if not cmd: raise ValueError("Command list must not be empty") diff --git a/src/visiongui/driver/save_debug_screenshot.py b/src/visiongui/driver/save_debug_screenshot.py index 95e6ffa..992164f 100644 --- a/src/visiongui/driver/save_debug_screenshot.py +++ b/src/visiongui/driver/save_debug_screenshot.py @@ -15,7 +15,7 @@ def save_debug_screenshot( image_file_name_prefix: FileNamePrefix, log_image_name: str, debug_output_base_path: str, -): +) -> None: file_binary = take_screenshot() file_name = f"{image_file_name_prefix.value}_{int(time.time())}_{os.path.basename(log_image_name)}.png" save_file( diff --git a/src/visiongui/driver/switch_to.py b/src/visiongui/driver/switch_to.py index be65f9c..20e74c0 100644 --- a/src/visiongui/driver/switch_to.py +++ b/src/visiongui/driver/switch_to.py @@ -4,8 +4,11 @@ def _set_foreground_hwnd(hwnd: int) -> None: - user32 = ctypes.WinDLL("user32", use_last_error=True) - kernel32 = ctypes.WinDLL("kernel32", use_last_error=True) + if platform.system() != "Windows": + raise RuntimeError("This function is only supported on Windows") + + user32 = ctypes.WinDLL("user32", use_last_error=True) # type: ignore[attr-defined] + kernel32 = ctypes.WinDLL("kernel32", use_last_error=True) # type: ignore[attr-defined] GetForegroundWindow = user32.GetForegroundWindow GetWindowThreadProcessId = user32.GetWindowThreadProcessId GetCurrentThreadId = kernel32.GetCurrentThreadId diff --git a/src/visiongui/driver/wait.py b/src/visiongui/driver/wait.py index 7aca356..c05855f 100644 --- a/src/visiongui/driver/wait.py +++ b/src/visiongui/driver/wait.py @@ -1,18 +1,21 @@ import time from collections.abc import Callable +from typing import TypeVar from visiongui.driver.exception import ExceptionTimeout +T = TypeVar("T") + class WebDriverWait: - def __init__(self, timeout: float, poll_frequency: float = 0.1): + def __init__(self, timeout: int, poll_frequency: float = 0.1): self.timeout = timeout self.poll_frequency = poll_frequency def until( self, - condition: Callable[[], object], - ): + condition: Callable[[], T], + ) -> T: end_time = time.time() + self.timeout while True: try: diff --git a/src/visiongui/driver/wait_for_window_to_disappear.py b/src/visiongui/driver/wait_for_window_to_disappear.py index 11c3b55..a5bbe1b 100644 --- a/src/visiongui/driver/wait_for_window_to_disappear.py +++ b/src/visiongui/driver/wait_for_window_to_disappear.py @@ -11,8 +11,8 @@ def wait_for_window_to_disappear( - title: re.Pattern, - timeout: float, + title: re.Pattern[str], + timeout: int, ) -> None: def _window_check() -> bool: return _get_matching_window(title) is None diff --git a/src/visiongui/element/action_type.py b/src/visiongui/element/action_type.py index d576816..4cef454 100644 --- a/src/visiongui/element/action_type.py +++ b/src/visiongui/element/action_type.py @@ -34,5 +34,4 @@ def action_type( wait = WebDriverWait(timeout=2) wait.until( condition=lambda: web_element.get_text() == text_to_type, - error_message=f"Element text did not match expected '{text_to_type}' after typing", ) diff --git a/src/visiongui/element/click_overlay.py b/src/visiongui/element/click_overlay.py index 8f10729..0e6e9a3 100644 --- a/src/visiongui/element/click_overlay.py +++ b/src/visiongui/element/click_overlay.py @@ -1,5 +1,6 @@ import threading import tkinter as tk +from typing import cast from visiongui.element.ClickOverlayOptions import ( ClickOverlayOptions, @@ -7,7 +8,7 @@ def click_overlay(x: int, y: int, options: ClickOverlayOptions) -> None: - def _show(): + def _show() -> None: overlay = tk.Tk() overlay.overrideredirect(True) overlay.attributes("-topmost", True) @@ -40,7 +41,7 @@ def _show(): shaft_bottom_right = (mid_x + shaft_half_width, arrow_height) shaft_bottom_left = (mid_x - shaft_half_width, arrow_height) - points = [ + points: list[tuple[float, float]] = [ arrowhead_tip, arrowhead_left, shaft_top_left, @@ -51,7 +52,10 @@ def _show(): ] canvas.create_polygon( - [tuple(round(coord) for coord in point) for point in points], + cast( + list[tuple[int, int]], + [tuple(round(coord) for coord in point) for point in points], + ), fill=options.fill_color, outline=options.border_color, width=options.border_width, diff --git a/src/visiongui/fixture_voltar_ao_vscode.py b/src/visiongui/fixture_voltar_ao_vscode.py index c6d4213..62b8a94 100644 --- a/src/visiongui/fixture_voltar_ao_vscode.py +++ b/src/visiongui/fixture_voltar_ao_vscode.py @@ -5,9 +5,10 @@ ) -def voltar_ao_vscode(desktop_driver: DesktopDriverInterface): +def voltar_ao_vscode(desktop_driver: DesktopDriverInterface) -> None: desktop_window = desktop_driver.find_window( title=re.compile("Visual Studio Code$"), timeout=2, ) + assert desktop_window is not None desktop_driver.switch_to(target_window=desktop_window)