From f106886a1c80de02b5eeb9c8ae05787d1e6769f4 Mon Sep 17 00:00:00 2001 From: Dmitriy Date: Thu, 23 May 2024 00:27:29 +0500 Subject: [PATCH] add type hints --- poetry.lock | 143 +++++++++--------- pyproject.toml | 7 +- pyzeebe/__init__.py | 22 ++- pyzeebe/channel/camunda_cloud_channel.py | 4 +- pyzeebe/channel/insecure_channel.py | 4 +- pyzeebe/channel/secure_channel.py | 4 +- pyzeebe/client/client.py | 21 ++- pyzeebe/client/sync_client.py | 13 +- pyzeebe/credentials/plugins.py | 2 +- pyzeebe/errors/credentials_errors.py | 4 +- pyzeebe/errors/job_errors.py | 6 +- pyzeebe/function_tools/__init__.py | 16 +- pyzeebe/function_tools/async_tools.py | 22 ++- pyzeebe/function_tools/dict_tools.py | 10 +- pyzeebe/function_tools/parameter_tools.py | 8 +- pyzeebe/grpc_internals/grpc_utils.py | 2 +- pyzeebe/grpc_internals/zeebe_adapter_base.py | 7 +- pyzeebe/grpc_internals/zeebe_job_adapter.py | 18 ++- .../grpc_internals/zeebe_message_adapter.py | 5 +- .../grpc_internals/zeebe_process_adapter.py | 17 ++- pyzeebe/job/job.py | 16 +- pyzeebe/py.typed | 0 pyzeebe/task/exception_handler.py | 2 +- pyzeebe/task/task.py | 11 +- pyzeebe/task/task_builder.py | 30 ++-- pyzeebe/task/task_config.py | 8 +- pyzeebe/types.py | 5 + pyzeebe/worker/job_executor.py | 6 +- pyzeebe/worker/job_poller.py | 13 +- pyzeebe/worker/task_router.py | 69 +++++++-- pyzeebe/worker/task_state.py | 5 +- pyzeebe/worker/worker.py | 5 +- 32 files changed, 315 insertions(+), 190 deletions(-) create mode 100644 pyzeebe/py.typed create mode 100644 pyzeebe/types.py diff --git a/poetry.lock b/poetry.lock index d42a659e..2b48485e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "aiofiles" @@ -112,13 +112,13 @@ files = [ [[package]] name = "certifi" -version = "2024.2.2" +version = "2024.6.2" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, - {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, + {file = "certifi-2024.6.2-py3-none-any.whl", hash = "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56"}, + {file = "certifi-2024.6.2.tar.gz", hash = "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516"}, ] [[package]] @@ -353,13 +353,13 @@ files = [ [[package]] name = "docutils" -version = "0.18.1" +version = "0.20.1" description = "Docutils -- Python Documentation Utilities" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.7" files = [ - {file = "docutils-0.18.1-py2.py3-none-any.whl", hash = "sha256:23010f129180089fbcd3bc08cfefccb3b890b0050e1ca00c867036e9d161b98c"}, - {file = "docutils-0.18.1.tar.gz", hash = "sha256:679987caf361a7539d76e584cbeddc311e3aee937877c87346f31debc63e9d06"}, + {file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"}, + {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"}, ] [[package]] @@ -378,61 +378,61 @@ test = ["pytest (>=6)"] [[package]] name = "grpcio" -version = "1.64.0" +version = "1.64.1" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.8" files = [ - {file = "grpcio-1.64.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:3b09c3d9de95461214a11d82cc0e6a46a6f4e1f91834b50782f932895215e5db"}, - {file = "grpcio-1.64.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:7e013428ab472892830287dd082b7d129f4d8afef49227a28223a77337555eaa"}, - {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:02cc9cc3f816d30f7993d0d408043b4a7d6a02346d251694d8ab1f78cc723e7e"}, - {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f5de082d936e0208ce8db9095821361dfa97af8767a6607ae71425ac8ace15c"}, - {file = "grpcio-1.64.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7b7bf346391dffa182fba42506adf3a84f4a718a05e445b37824136047686a1"}, - {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b2cbdfba18408389a1371f8c2af1659119e1831e5ed24c240cae9e27b4abc38d"}, - {file = "grpcio-1.64.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aca4f15427d2df592e0c8f3d38847e25135e4092d7f70f02452c0e90d6a02d6d"}, - {file = "grpcio-1.64.0-cp310-cp310-win32.whl", hash = "sha256:7c1f5b2298244472bcda49b599be04579f26425af0fd80d3f2eb5fd8bc84d106"}, - {file = "grpcio-1.64.0-cp310-cp310-win_amd64.whl", hash = "sha256:73f84f9e5985a532e47880b3924867de16fa1aa513fff9b26106220c253c70c5"}, - {file = "grpcio-1.64.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:2a18090371d138a57714ee9bffd6c9c9cb2e02ce42c681aac093ae1e7189ed21"}, - {file = "grpcio-1.64.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:59c68df3a934a586c3473d15956d23a618b8f05b5e7a3a904d40300e9c69cbf0"}, - {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:b52e1ec7185512103dd47d41cf34ea78e7a7361ba460187ddd2416b480e0938c"}, - {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d598b5d5e2c9115d7fb7e2cb5508d14286af506a75950762aa1372d60e41851"}, - {file = "grpcio-1.64.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01615bbcae6875eee8091e6b9414072f4e4b00d8b7e141f89635bdae7cf784e5"}, - {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0b2dfe6dcace264807d9123d483d4c43274e3f8c39f90ff51de538245d7a4145"}, - {file = "grpcio-1.64.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7f17572dc9acd5e6dfd3014d10c0b533e9f79cd9517fc10b0225746f4c24b58e"}, - {file = "grpcio-1.64.0-cp311-cp311-win32.whl", hash = "sha256:6ec5ed15b4ffe56e2c6bc76af45e6b591c9be0224b3fb090adfb205c9012367d"}, - {file = "grpcio-1.64.0-cp311-cp311-win_amd64.whl", hash = "sha256:597191370951b477b7a1441e1aaa5cacebeb46a3b0bd240ec3bb2f28298c7553"}, - {file = "grpcio-1.64.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:1ce4cd5a61d4532651079e7aae0fedf9a80e613eed895d5b9743e66b52d15812"}, - {file = "grpcio-1.64.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:650a8150a9b288f40d5b7c1d5400cc11724eae50bd1f501a66e1ea949173649b"}, - {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:8de0399b983f8676a7ccfdd45e5b2caec74a7e3cc576c6b1eecf3b3680deda5e"}, - {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46b8b43ba6a2a8f3103f103f97996cad507bcfd72359af6516363c48793d5a7b"}, - {file = "grpcio-1.64.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a54362f03d4dcfae63be455d0a7d4c1403673498b92c6bfe22157d935b57c7a9"}, - {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1f8ea18b928e539046bb5f9c124d717fbf00cc4b2d960ae0b8468562846f5aa1"}, - {file = "grpcio-1.64.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c56c91bd2923ddb6e7ed28ebb66d15633b03e0df22206f22dfcdde08047e0a48"}, - {file = "grpcio-1.64.0-cp312-cp312-win32.whl", hash = "sha256:874c741c8a66f0834f653a69e7e64b4e67fcd4a8d40296919b93bab2ccc780ba"}, - {file = "grpcio-1.64.0-cp312-cp312-win_amd64.whl", hash = "sha256:0da1d921f8e4bcee307aeef6c7095eb26e617c471f8cb1c454fd389c5c296d1e"}, - {file = "grpcio-1.64.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:c46fb6bfca17bfc49f011eb53416e61472fa96caa0979b4329176bdd38cbbf2a"}, - {file = "grpcio-1.64.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3d2004e85cf5213995d09408501f82c8534700d2babeb81dfdba2a3bff0bb396"}, - {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:6d5541eb460d73a07418524fb64dcfe0adfbcd32e2dac0f8f90ce5b9dd6c046c"}, - {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f279ad72dd7d64412e10f2443f9f34872a938c67387863c4cd2fb837f53e7d2"}, - {file = "grpcio-1.64.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85fda90b81da25993aa47fae66cae747b921f8f6777550895fb62375b776a231"}, - {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a053584079b793a54bece4a7d1d1b5c0645bdbee729215cd433703dc2532f72b"}, - {file = "grpcio-1.64.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:579dd9fb11bc73f0de061cab5f8b2def21480fd99eb3743ed041ad6a1913ee2f"}, - {file = "grpcio-1.64.0-cp38-cp38-win32.whl", hash = "sha256:23b6887bb21d77649d022fa1859e05853fdc2e60682fd86c3db652a555a282e0"}, - {file = "grpcio-1.64.0-cp38-cp38-win_amd64.whl", hash = "sha256:753cb58683ba0c545306f4e17dabf468d29cb6f6b11832e1e432160bb3f8403c"}, - {file = "grpcio-1.64.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:2186d76a7e383e1466e0ea2b0febc343ffeae13928c63c6ec6826533c2d69590"}, - {file = "grpcio-1.64.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0f30596cdcbed3c98024fb4f1d91745146385b3f9fd10c9f2270cbfe2ed7ed91"}, - {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:d9171f025a196f5bcfec7e8e7ffb7c3535f7d60aecd3503f9e250296c7cfc150"}, - {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf4c8daed18ae2be2f1fc7d613a76ee2a2e28fdf2412d5c128be23144d28283d"}, - {file = "grpcio-1.64.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3550493ac1d23198d46dc9c9b24b411cef613798dc31160c7138568ec26bc9b4"}, - {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3161a8f8bb38077a6470508c1a7301cd54301c53b8a34bb83e3c9764874ecabd"}, - {file = "grpcio-1.64.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e8fabe2cc57a369638ab1ad8e6043721014fdf9a13baa7c0e35995d3a4a7618"}, - {file = "grpcio-1.64.0-cp39-cp39-win32.whl", hash = "sha256:31890b24d47b62cc27da49a462efe3d02f3c120edb0e6c46dcc0025506acf004"}, - {file = "grpcio-1.64.0-cp39-cp39-win_amd64.whl", hash = "sha256:5a56797dea8c02e7d3a85dfea879f286175cf4d14fbd9ab3ef2477277b927baa"}, - {file = "grpcio-1.64.0.tar.gz", hash = "sha256:257baf07f53a571c215eebe9679c3058a313fd1d1f7c4eede5a8660108c52d9c"}, + {file = "grpcio-1.64.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:55697ecec192bc3f2f3cc13a295ab670f51de29884ca9ae6cd6247df55df2502"}, + {file = "grpcio-1.64.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:3b64ae304c175671efdaa7ec9ae2cc36996b681eb63ca39c464958396697daff"}, + {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:bac71b4b28bc9af61efcdc7630b166440bbfbaa80940c9a697271b5e1dabbc61"}, + {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c024ffc22d6dc59000faf8ad781696d81e8e38f4078cb0f2630b4a3cf231a90"}, + {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7cd5c1325f6808b8ae31657d281aadb2a51ac11ab081ae335f4f7fc44c1721d"}, + {file = "grpcio-1.64.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0a2813093ddb27418a4c99f9b1c223fab0b053157176a64cc9db0f4557b69bd9"}, + {file = "grpcio-1.64.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2981c7365a9353f9b5c864595c510c983251b1ab403e05b1ccc70a3d9541a73b"}, + {file = "grpcio-1.64.1-cp310-cp310-win32.whl", hash = "sha256:1262402af5a511c245c3ae918167eca57342c72320dffae5d9b51840c4b2f86d"}, + {file = "grpcio-1.64.1-cp310-cp310-win_amd64.whl", hash = "sha256:19264fc964576ddb065368cae953f8d0514ecc6cb3da8903766d9fb9d4554c33"}, + {file = "grpcio-1.64.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:58b1041e7c870bb30ee41d3090cbd6f0851f30ae4eb68228955d973d3efa2e61"}, + {file = "grpcio-1.64.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bbc5b1d78a7822b0a84c6f8917faa986c1a744e65d762ef6d8be9d75677af2ca"}, + {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:5841dd1f284bd1b3d8a6eca3a7f062b06f1eec09b184397e1d1d43447e89a7ae"}, + {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8caee47e970b92b3dd948371230fcceb80d3f2277b3bf7fbd7c0564e7d39068e"}, + {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73819689c169417a4f978e562d24f2def2be75739c4bed1992435d007819da1b"}, + {file = "grpcio-1.64.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6503b64c8b2dfad299749cad1b595c650c91e5b2c8a1b775380fcf8d2cbba1e9"}, + {file = "grpcio-1.64.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1de403fc1305fd96cfa75e83be3dee8538f2413a6b1685b8452301c7ba33c294"}, + {file = "grpcio-1.64.1-cp311-cp311-win32.whl", hash = "sha256:d4d29cc612e1332237877dfa7fe687157973aab1d63bd0f84cf06692f04c0367"}, + {file = "grpcio-1.64.1-cp311-cp311-win_amd64.whl", hash = "sha256:5e56462b05a6f860b72f0fa50dca06d5b26543a4e88d0396259a07dc30f4e5aa"}, + {file = "grpcio-1.64.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:4657d24c8063e6095f850b68f2d1ba3b39f2b287a38242dcabc166453e950c59"}, + {file = "grpcio-1.64.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:62b4e6eb7bf901719fce0ca83e3ed474ae5022bb3827b0a501e056458c51c0a1"}, + {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:ee73a2f5ca4ba44fa33b4d7d2c71e2c8a9e9f78d53f6507ad68e7d2ad5f64a22"}, + {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:198908f9b22e2672a998870355e226a725aeab327ac4e6ff3a1399792ece4762"}, + {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39b9d0acaa8d835a6566c640f48b50054f422d03e77e49716d4c4e8e279665a1"}, + {file = "grpcio-1.64.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5e42634a989c3aa6049f132266faf6b949ec2a6f7d302dbb5c15395b77d757eb"}, + {file = "grpcio-1.64.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b1a82e0b9b3022799c336e1fc0f6210adc019ae84efb7321d668129d28ee1efb"}, + {file = "grpcio-1.64.1-cp312-cp312-win32.whl", hash = "sha256:55260032b95c49bee69a423c2f5365baa9369d2f7d233e933564d8a47b893027"}, + {file = "grpcio-1.64.1-cp312-cp312-win_amd64.whl", hash = "sha256:c1a786ac592b47573a5bb7e35665c08064a5d77ab88a076eec11f8ae86b3e3f6"}, + {file = "grpcio-1.64.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:a011ac6c03cfe162ff2b727bcb530567826cec85eb8d4ad2bfb4bd023287a52d"}, + {file = "grpcio-1.64.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:4d6dab6124225496010bd22690f2d9bd35c7cbb267b3f14e7a3eb05c911325d4"}, + {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:a5e771d0252e871ce194d0fdcafd13971f1aae0ddacc5f25615030d5df55c3a2"}, + {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c3c1b90ab93fed424e454e93c0ed0b9d552bdf1b0929712b094f5ecfe7a23ad"}, + {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20405cb8b13fd779135df23fabadc53b86522d0f1cba8cca0e87968587f50650"}, + {file = "grpcio-1.64.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0cc79c982ccb2feec8aad0e8fb0d168bcbca85bc77b080d0d3c5f2f15c24ea8f"}, + {file = "grpcio-1.64.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a3a035c37ce7565b8f4f35ff683a4db34d24e53dc487e47438e434eb3f701b2a"}, + {file = "grpcio-1.64.1-cp38-cp38-win32.whl", hash = "sha256:1257b76748612aca0f89beec7fa0615727fd6f2a1ad580a9638816a4b2eb18fd"}, + {file = "grpcio-1.64.1-cp38-cp38-win_amd64.whl", hash = "sha256:0a12ddb1678ebc6a84ec6b0487feac020ee2b1659cbe69b80f06dbffdb249122"}, + {file = "grpcio-1.64.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:75dbbf415026d2862192fe1b28d71f209e2fd87079d98470db90bebe57b33179"}, + {file = "grpcio-1.64.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e3d9f8d1221baa0ced7ec7322a981e28deb23749c76eeeb3d33e18b72935ab62"}, + {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:5f8b75f64d5d324c565b263c67dbe4f0af595635bbdd93bb1a88189fc62ed2e5"}, + {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c84ad903d0d94311a2b7eea608da163dace97c5fe9412ea311e72c3684925602"}, + {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:940e3ec884520155f68a3b712d045e077d61c520a195d1a5932c531f11883489"}, + {file = "grpcio-1.64.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f10193c69fc9d3d726e83bbf0f3d316f1847c3071c8c93d8090cf5f326b14309"}, + {file = "grpcio-1.64.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ac15b6c2c80a4d1338b04d42a02d376a53395ddf0ec9ab157cbaf44191f3ffdd"}, + {file = "grpcio-1.64.1-cp39-cp39-win32.whl", hash = "sha256:03b43d0ccf99c557ec671c7dede64f023c7da9bb632ac65dbc57f166e4970040"}, + {file = "grpcio-1.64.1-cp39-cp39-win_amd64.whl", hash = "sha256:ed6091fa0adcc7e4ff944090cf203a52da35c37a130efa564ded02b7aff63bcd"}, + {file = "grpcio-1.64.1.tar.gz", hash = "sha256:8d51dd1c59d5fa0f34266b80a3805ec29a1f26425c2a54736133f6d87fc4968a"}, ] [package.extras] -protobuf = ["grpcio-tools (>=1.64.0)"] +protobuf = ["grpcio-tools (>=1.64.1)"] [[package]] name = "idna" @@ -895,7 +895,6 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -932,13 +931,13 @@ files = [ [[package]] name = "requests" -version = "2.32.2" +version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" files = [ - {file = "requests-2.32.2-py3-none-any.whl", hash = "sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c"}, - {file = "requests-2.32.2.tar.gz", hash = "sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289"}, + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] [package.dependencies] @@ -1202,13 +1201,13 @@ files = [ [[package]] name = "types-requests" -version = "2.32.0.20240523" +version = "2.32.0.20240602" description = "Typing stubs for requests" optional = false python-versions = ">=3.8" files = [ - {file = "types-requests-2.32.0.20240523.tar.gz", hash = "sha256:26b8a6de32d9f561192b9942b41c0ab2d8010df5677ca8aa146289d11d505f57"}, - {file = "types_requests-2.32.0.20240523-py3-none-any.whl", hash = "sha256:f19ed0e2daa74302069bbbbf9e82902854ffa780bc790742a810a9aaa52f65ec"}, + {file = "types-requests-2.32.0.20240602.tar.gz", hash = "sha256:3f98d7bbd0dd94ebd10ff43a7fbe20c3b8528acace6d8efafef0b6a184793f06"}, + {file = "types_requests-2.32.0.20240602-py3-none-any.whl", hash = "sha256:ed3946063ea9fbc6b5fc0c44fa279188bae42d582cb63760be6cb4b9d06c3de8"}, ] [package.dependencies] @@ -1231,13 +1230,13 @@ types-requests = "*" [[package]] name = "typing-extensions" -version = "4.12.1" +version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.12.1-py3-none-any.whl", hash = "sha256:6024b58b69089e5a89c347397254e35f1bf02a907728ec7fee9bf0fe837d203a"}, - {file = "typing_extensions-4.12.1.tar.gz", hash = "sha256:915f5e35ff76f56588223f15fdd5938f9a1cf9195c0de25130c627e4d597f6d1"}, + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [[package]] @@ -1274,20 +1273,20 @@ protobuf = ">=4.21,<5.0" [[package]] name = "zipp" -version = "3.18.2" +version = "3.19.2" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ - {file = "zipp-3.18.2-py3-none-any.whl", hash = "sha256:dce197b859eb796242b0622af1b8beb0a722d52aa2f57133ead08edd5bf5374e"}, - {file = "zipp-3.18.2.tar.gz", hash = "sha256:6278d9ddbcfb1f1089a88fde84481528b07b0e10474e09dcfe53dad4069fa059"}, + {file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"}, + {file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "61c4b07415587f61816ce17445289c2af5c353b4833aebabcc3930b8587fa251" +content-hash = "4eee918949e3f1b3499c971b9c01fbbec5f897d99128d85da653dd069ce87f37" diff --git a/pyproject.toml b/pyproject.toml index d81ab5b9..ec50082b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ oauthlib = "^3.1.0" requests-oauthlib = ">=1.3.0,<3.0.0" aiofiles = ">=0.7,<24" zeebe-grpc = "^8.4.0" -typing-extensions = "^4.5.0" +typing-extensions = "^4.11.0" [tool.poetry.group.dev.dependencies] pytest = ">=7.4,<9.0" @@ -33,7 +33,7 @@ pytest-grpc = "^0.8.0" pytest-mock = "^3.11.1" pylint = ">=2.17.5,<4.0.0" black = "^23.7.0" -mypy = "^1.4.1" +mypy = "^1.10.0" coveralls = "^3.3.1" responses = ">=0.23.2,<0.26.0" bump2version = "^1.0.1" @@ -48,6 +48,8 @@ types-requests-oauthlib = ">=1.3.0,<3.0.0" [tool.mypy] python_version = "3.8" +packages = ["pyzeebe"] +strict = true [[tool.mypy.overrides]] module = [ @@ -57,7 +59,6 @@ module = [ ] ignore_missing_imports = true - [tool.pylint.master] max-line-length = 120 disable = ["C0114", "C0115", "C0116"] diff --git a/pyzeebe/__init__.py b/pyzeebe/__init__.py index e8dffcc4..5de1d3e9 100644 --- a/pyzeebe/__init__.py +++ b/pyzeebe/__init__.py @@ -3,7 +3,7 @@ from pyzeebe import errors from pyzeebe.channel import * from pyzeebe.client.client import ZeebeClient -from pyzeebe.client.sync_client import SyncZeebeClient # type: ignore +from pyzeebe.client.sync_client import SyncZeebeClient from pyzeebe.credentials.base import CredentialsABC from pyzeebe.credentials.camunda_identity import CamundaIdentityCredentials from pyzeebe.credentials.plugins import AuthMetadataPlugin @@ -14,3 +14,23 @@ from pyzeebe.task.types import TaskDecorator from pyzeebe.worker.task_router import ZeebeTaskRouter from pyzeebe.worker.worker import ZeebeWorker + +__all__ = ( + "errors", + "create_camunda_cloud_channel", + "create_insecure_channel", + "create_secure_channel", + "ZeebeClient", + "SyncZeebeClient", + "Job", + "JobStatus", + "ExceptionHandler", + "TaskConfig", + "TaskDecorator", + "ZeebeTaskRouter", + "default_exception_handler", + "ZeebeWorker", + "CredentialsABC", + "CamundaIdentityCredentials", + "AuthMetadataPlugin", +) diff --git a/pyzeebe/channel/camunda_cloud_channel.py b/pyzeebe/channel/camunda_cloud_channel.py index 8e36feaa..b524405b 100644 --- a/pyzeebe/channel/camunda_cloud_channel.py +++ b/pyzeebe/channel/camunda_cloud_channel.py @@ -73,7 +73,9 @@ def _get_access_token(url: str, client_id: str, client_secret: str, audience: st }, ) response.raise_for_status() - return response.json()["access_token"] + access_token = response.json()["access_token"] + assert isinstance(access_token, str) + return access_token except HTTPError as http_error: raise InvalidOAuthCredentialsError(url=url, client_id=client_id, audience=audience) from http_error diff --git a/pyzeebe/channel/insecure_channel.py b/pyzeebe/channel/insecure_channel.py index 74d7f5cd..fb3ddf14 100644 --- a/pyzeebe/channel/insecure_channel.py +++ b/pyzeebe/channel/insecure_channel.py @@ -1,4 +1,4 @@ -from typing import Dict, Optional +from typing import Any, Dict, Optional import grpc @@ -7,7 +7,7 @@ def create_insecure_channel( - hostname: Optional[str] = None, port: Optional[int] = None, channel_options: Optional[Dict] = None + hostname: Optional[str] = None, port: Optional[int] = None, channel_options: Optional[Dict[str, Any]] = None ) -> grpc.aio.Channel: """ Create an insecure channel diff --git a/pyzeebe/channel/secure_channel.py b/pyzeebe/channel/secure_channel.py index 0e4dfff8..5765eb83 100644 --- a/pyzeebe/channel/secure_channel.py +++ b/pyzeebe/channel/secure_channel.py @@ -1,4 +1,4 @@ -from typing import Dict, Optional +from typing import Any, Dict, Optional import grpc @@ -9,7 +9,7 @@ def create_secure_channel( hostname: Optional[str] = None, port: Optional[int] = None, - channel_options: Optional[Dict] = None, + channel_options: Optional[Dict[str, Any]] = None, channel_credentials: Optional[grpc.ChannelCredentials] = None, ) -> grpc.aio.Channel: """ diff --git a/pyzeebe/client/client.py b/pyzeebe/client/client.py index af775608..975a66b0 100644 --- a/pyzeebe/client/client.py +++ b/pyzeebe/client/client.py @@ -1,15 +1,16 @@ -from typing import Dict, List, Optional, Tuple +from typing import Any, Dict, Iterable, Optional, Tuple import grpc from typing_extensions import deprecated from pyzeebe.grpc_internals.zeebe_adapter import ZeebeAdapter +from pyzeebe.types import Variables -class ZeebeClient(object): +class ZeebeClient: """A zeebe client that can connect to a zeebe instance and perform actions.""" - def __init__(self, grpc_channel: grpc.aio.Channel, max_connection_retries: int = 10): + def __init__(self, grpc_channel: grpc.aio.Channel, max_connection_retries: int = 10) -> None: """ Args: grpc_channel (grpc.aio.Channel): GRPC Channel connected to a Zeebe gateway @@ -19,7 +20,11 @@ def __init__(self, grpc_channel: grpc.aio.Channel, max_connection_retries: int = self.zeebe_adapter = ZeebeAdapter(grpc_channel, max_connection_retries) async def run_process( - self, bpmn_process_id: str, variables: Optional[Dict] = None, version: int = -1, tenant_id: Optional[str] = None + self, + bpmn_process_id: str, + variables: Optional[Variables] = None, + version: int = -1, + tenant_id: Optional[str] = None, ) -> int: """ Run process @@ -50,12 +55,12 @@ async def run_process( async def run_process_with_result( self, bpmn_process_id: str, - variables: Optional[Dict] = None, + variables: Optional[Variables] = None, version: int = -1, timeout: int = 0, - variables_to_fetch: Optional[List[str]] = None, + variables_to_fetch: Optional[Iterable[str]] = None, tenant_id: Optional[str] = None, - ) -> Tuple[int, Dict]: + ) -> Tuple[int, Dict[str, Any]]: """ Run process and wait for the result. @@ -153,7 +158,7 @@ async def publish_message( self, name: str, correlation_key: str, - variables: Optional[Dict] = None, + variables: Optional[Variables] = None, time_to_live_in_milliseconds: int = 60000, message_id: Optional[str] = None, tenant_id: Optional[str] = None, diff --git a/pyzeebe/client/sync_client.py b/pyzeebe/client/sync_client.py index 1dc6ad22..edcadd45 100644 --- a/pyzeebe/client/sync_client.py +++ b/pyzeebe/client/sync_client.py @@ -1,21 +1,22 @@ import asyncio -from typing import Dict, List, Optional, Tuple +from typing import Any, Dict, List, Optional, Tuple import grpc from typing_extensions import deprecated from pyzeebe import ZeebeClient +from pyzeebe.types import Variables class SyncZeebeClient: - def __init__(self, grpc_channel: grpc.aio.Channel, max_connection_retries: int = 10): + def __init__(self, grpc_channel: grpc.aio.Channel, max_connection_retries: int = 10) -> None: self.loop = asyncio.get_event_loop() self.client = ZeebeClient(grpc_channel, max_connection_retries) def run_process( self, bpmn_process_id: str, - variables: Optional[Dict] = None, + variables: Optional[Variables] = None, version: int = -1, tenant_id: Optional[str] = None, ) -> int: @@ -24,12 +25,12 @@ def run_process( def run_process_with_result( self, bpmn_process_id: str, - variables: Optional[Dict] = None, + variables: Optional[Variables] = None, version: int = -1, timeout: int = 0, variables_to_fetch: Optional[List[str]] = None, tenant_id: Optional[str] = None, - ) -> Tuple[int, Dict]: + ) -> Tuple[int, Dict[str, Any]]: return self.loop.run_until_complete( self.client.run_process_with_result( bpmn_process_id, variables, version, timeout, variables_to_fetch, tenant_id @@ -50,7 +51,7 @@ def publish_message( self, name: str, correlation_key: str, - variables: Optional[Dict] = None, + variables: Optional[Variables] = None, time_to_live_in_milliseconds: int = 60000, message_id: Optional[str] = None, tenant_id: Optional[str] = None, diff --git a/pyzeebe/credentials/plugins.py b/pyzeebe/credentials/plugins.py index 9f30f9f6..af62e34e 100644 --- a/pyzeebe/credentials/plugins.py +++ b/pyzeebe/credentials/plugins.py @@ -3,7 +3,7 @@ from pyzeebe.credentials.base import CredentialsABC -class AuthMetadataPlugin(grpc.AuthMetadataPlugin): +class AuthMetadataPlugin(grpc.AuthMetadataPlugin): # type: ignore[misc] """Custom authentication plugin with exception catching. Args: diff --git a/pyzeebe/errors/credentials_errors.py b/pyzeebe/errors/credentials_errors.py index b3ca7942..b557c009 100644 --- a/pyzeebe/errors/credentials_errors.py +++ b/pyzeebe/errors/credentials_errors.py @@ -2,12 +2,12 @@ class InvalidOAuthCredentialsError(PyZeebeError): - def __init__(self, url: str, client_id: str, audience: str): + def __init__(self, url: str, client_id: str, audience: str) -> None: super().__init__( f"Invalid OAuth credentials supplied for {url} with audience {audience} and client id {client_id}" ) class InvalidCamundaCloudCredentialsError(PyZeebeError): - def __init__(self, client_id: str, cluster_id: str): + def __init__(self, client_id: str, cluster_id: str) -> None: super().__init__(f"Invalid credentials supplied for cluster {cluster_id} with client {client_id}") diff --git a/pyzeebe/errors/job_errors.py b/pyzeebe/errors/job_errors.py index 849b430a..19b2204b 100644 --- a/pyzeebe/errors/job_errors.py +++ b/pyzeebe/errors/job_errors.py @@ -2,7 +2,7 @@ class ActivateJobsRequestInvalidError(PyZeebeError): - def __init__(self, task_type: str, worker: str, timeout: int, max_jobs_to_activate: int): + def __init__(self, task_type: str, worker: str, timeout: int, max_jobs_to_activate: int) -> None: msg = "Failed to activate jobs. Reasons:" if task_type == "" or task_type is None: msg = msg + "task_type is empty, " @@ -17,12 +17,12 @@ def __init__(self, task_type: str, worker: str, timeout: int, max_jobs_to_activa class JobAlreadyDeactivatedError(PyZeebeError): - def __init__(self, job_key: int): + def __init__(self, job_key: int) -> None: super().__init__(f"Job {job_key} was already stopped (Completed/Failed/Error)") self.job_key = job_key class JobNotFoundError(PyZeebeError): - def __init__(self, job_key: int): + def __init__(self, job_key: int) -> None: super().__init__(f"Job {job_key} not found") self.job_key = job_key diff --git a/pyzeebe/function_tools/__init__.py b/pyzeebe/function_tools/__init__.py index 09c43635..fd9d708a 100644 --- a/pyzeebe/function_tools/__init__.py +++ b/pyzeebe/function_tools/__init__.py @@ -1,10 +1,14 @@ -from typing import Awaitable, Callable, Dict, TypeVar, Union +from __future__ import annotations -Parameters = TypeVar("Parameters") +from typing import Any, Awaitable, Callable, Dict, Optional, TypeVar, Union + +from typing_extensions import ParamSpec + +Parameters = ParamSpec("Parameters") ReturnType = TypeVar("ReturnType") -SyncFunction = Callable[[Parameters], ReturnType] -AsyncFunction = Callable[[Parameters], Awaitable[ReturnType]] -Function = Union[SyncFunction, AsyncFunction] +SyncFunction = Callable[Parameters, ReturnType] +AsyncFunction = Callable[Parameters, Awaitable[ReturnType]] +Function = Union[SyncFunction[Parameters, ReturnType], AsyncFunction[Parameters, ReturnType]] -DictFunction = Callable[[Parameters], Awaitable[Dict]] +DictFunction = Callable[Parameters, Awaitable[Optional[Dict[str, Any]]]] diff --git a/pyzeebe/function_tools/async_tools.py b/pyzeebe/function_tools/async_tools.py index b16625b8..80338503 100644 --- a/pyzeebe/function_tools/async_tools.py +++ b/pyzeebe/function_tools/async_tools.py @@ -1,31 +1,37 @@ +from __future__ import annotations + import asyncio import functools -from typing import List +from typing import Any, Iterable, List, TypeVar + +from typing_extensions import ParamSpec, TypeIs from pyzeebe.function_tools import AsyncFunction, Function, SyncFunction +P = ParamSpec("P") +R = TypeVar("R") + -def asyncify_all_functions(functions: List[Function]) -> List[AsyncFunction]: - async_functions = [] +def asyncify_all_functions(functions: Iterable[Function[..., Any]]) -> List[AsyncFunction[..., Any]]: + async_functions: list[AsyncFunction[..., Any]] = [] for function in functions: if not is_async_function(function): async_functions.append(asyncify(function)) else: - # Mypy doesn't correctly understand that this is an async function - async_functions.append(function) # type: ignore + async_functions.append(function) return async_functions -def asyncify(task_function: SyncFunction) -> AsyncFunction: +def asyncify(task_function: SyncFunction[P, R]) -> AsyncFunction[P, R]: @functools.wraps(task_function) - async def async_function(*args, **kwargs): + async def async_function(*args: P.args, **kwargs: P.kwargs) -> R: loop = asyncio.get_event_loop() return await loop.run_in_executor(None, functools.partial(task_function, *args, **kwargs)) return async_function -def is_async_function(function: Function) -> bool: +def is_async_function(function: Function[P, R]) -> TypeIs[AsyncFunction[P, R]]: # Not using inspect.iscoroutinefunction here because it doens't handle AsyncMock well # See: https://bugs.python.org/issue40573 return asyncio.iscoroutinefunction(function) diff --git a/pyzeebe/function_tools/dict_tools.py b/pyzeebe/function_tools/dict_tools.py index bfb8787d..4dd9f3b2 100644 --- a/pyzeebe/function_tools/dict_tools.py +++ b/pyzeebe/function_tools/dict_tools.py @@ -1,11 +1,17 @@ import functools +from typing import Any, Dict, TypeVar + +from typing_extensions import ParamSpec from pyzeebe.function_tools import AsyncFunction, DictFunction +P = ParamSpec("P") +R = TypeVar("R") + -def convert_to_dict_function(single_value_function: AsyncFunction, variable_name: str) -> DictFunction: +def convert_to_dict_function(single_value_function: AsyncFunction[P, R], variable_name: str) -> DictFunction[P]: @functools.wraps(single_value_function) - async def inner_fn(*args, **kwargs): + async def inner_fn(*args: P.args, **kwargs: P.kwargs) -> Dict[str, Any]: return {variable_name: await single_value_function(*args, **kwargs)} return inner_fn diff --git a/pyzeebe/function_tools/parameter_tools.py b/pyzeebe/function_tools/parameter_tools.py index da61fcbd..fb2fabf5 100644 --- a/pyzeebe/function_tools/parameter_tools.py +++ b/pyzeebe/function_tools/parameter_tools.py @@ -1,11 +1,13 @@ +from __future__ import annotations + import inspect -from typing import List, Optional +from typing import Any, List, Optional from pyzeebe.function_tools import Function from pyzeebe.job.job import Job -def get_parameters_from_function(task_function: Function) -> Optional[List[str]]: +def get_parameters_from_function(task_function: Function[..., Any]) -> Optional[List[str]]: function_signature = inspect.signature(task_function) for _, parameter in function_signature.parameters.items(): if parameter.kind in (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD): @@ -20,7 +22,7 @@ def get_parameters_from_function(task_function: Function) -> Optional[List[str]] return [param.name for param in function_signature.parameters.values() if param.annotation != Job] -def get_job_parameter_name(function: Function) -> Optional[str]: +def get_job_parameter_name(function: Function[..., Any]) -> Optional[str]: function_signature = inspect.signature(function) params = list(function_signature.parameters.values()) for param in params: diff --git a/pyzeebe/grpc_internals/grpc_utils.py b/pyzeebe/grpc_internals/grpc_utils.py index d21c0dbb..7be373b3 100644 --- a/pyzeebe/grpc_internals/grpc_utils.py +++ b/pyzeebe/grpc_internals/grpc_utils.py @@ -1,5 +1,5 @@ import grpc -def is_error_status(rpc_error: grpc.aio.AioRpcError, *status_codes: grpc.StatusCode): +def is_error_status(rpc_error: grpc.aio.AioRpcError, *status_codes: grpc.StatusCode) -> bool: return rpc_error.code() in status_codes diff --git a/pyzeebe/grpc_internals/zeebe_adapter_base.py b/pyzeebe/grpc_internals/zeebe_adapter_base.py index 5327344d..740b15c1 100644 --- a/pyzeebe/grpc_internals/zeebe_adapter_base.py +++ b/pyzeebe/grpc_internals/zeebe_adapter_base.py @@ -1,4 +1,5 @@ import logging +from typing import NoReturn import grpc from zeebe_grpc.gateway_pb2_grpc import GatewayStub @@ -24,10 +25,10 @@ def __init__(self, grpc_channel: grpc.aio.Channel, max_connection_retries: int = self._max_connection_retries = max_connection_retries self._current_connection_retries = 0 - def _should_retry(self): + def _should_retry(self) -> bool: return self._max_connection_retries == -1 or self._current_connection_retries < self._max_connection_retries - async def _handle_grpc_error(self, grpc_error: grpc.aio.AioRpcError): + async def _handle_grpc_error(self, grpc_error: grpc.aio.AioRpcError) -> NoReturn: try: pyzeebe_error = _create_pyzeebe_error_from_grpc_error(grpc_error) raise pyzeebe_error @@ -37,7 +38,7 @@ async def _handle_grpc_error(self, grpc_error: grpc.aio.AioRpcError): await self._close() raise - async def _close(self): + async def _close(self) -> None: try: await self._channel.close() except Exception as exception: diff --git a/pyzeebe/grpc_internals/zeebe_job_adapter.py b/pyzeebe/grpc_internals/zeebe_job_adapter.py index 67d03ea2..f259ea77 100644 --- a/pyzeebe/grpc_internals/zeebe_job_adapter.py +++ b/pyzeebe/grpc_internals/zeebe_job_adapter.py @@ -1,9 +1,10 @@ import json import logging -from typing import AsyncGenerator, Dict, List, Optional +from typing import AsyncGenerator, Iterable, Optional import grpc from zeebe_grpc.gateway_pb2 import ( + ActivatedJob, ActivateJobsRequest, CompleteJobRequest, CompleteJobResponse, @@ -21,6 +22,7 @@ from pyzeebe.grpc_internals.grpc_utils import is_error_status from pyzeebe.grpc_internals.zeebe_adapter_base import ZeebeAdapterBase from pyzeebe.job.job import Job +from pyzeebe.types import Variables logger = logging.getLogger(__name__) @@ -32,9 +34,9 @@ async def activate_jobs( worker: str, timeout: int, max_jobs_to_activate: int, - variables_to_fetch: List[str], + variables_to_fetch: Iterable[str], request_timeout: int, - tenant_ids: Optional[List[str]] = None, + tenant_ids: Optional[Iterable[str]] = None, ) -> AsyncGenerator[Job, None]: try: async for response in self._gateway_stub.ActivateJobs( @@ -57,7 +59,7 @@ async def activate_jobs( raise ActivateJobsRequestInvalidError(task_type, worker, timeout, max_jobs_to_activate) from grpc_error await self._handle_grpc_error(grpc_error) - def _create_job_from_raw_job(self, response) -> Job: + def _create_job_from_raw_job(self, response: ActivatedJob) -> Job: return Job( key=response.key, type=response.type, @@ -73,10 +75,10 @@ def _create_job_from_raw_job(self, response) -> Job: deadline=response.deadline, variables=json.loads(response.variables), tenant_id=response.tenantId, - zeebe_adapter=self, + zeebe_adapter=self, # type: ignore[arg-type] ) - async def complete_job(self, job_key: int, variables: Dict) -> CompleteJobResponse: + async def complete_job(self, job_key: int, variables: Variables) -> CompleteJobResponse: try: return await self._gateway_stub.CompleteJob( CompleteJobRequest(jobKey=job_key, variables=json.dumps(variables)) @@ -89,7 +91,7 @@ async def complete_job(self, job_key: int, variables: Dict) -> CompleteJobRespon await self._handle_grpc_error(grpc_error) async def fail_job( - self, job_key: int, retries: int, message: str, retry_back_off_ms: int, variables: Dict + self, job_key: int, retries: int, message: str, retry_back_off_ms: int, variables: Variables ) -> FailJobResponse: try: return await self._gateway_stub.FailJob( @@ -109,7 +111,7 @@ async def fail_job( await self._handle_grpc_error(grpc_error) async def throw_error( - self, job_key: int, message: str, variables: Dict, error_code: str = "" + self, job_key: int, message: str, variables: Variables, error_code: str = "" ) -> ThrowErrorResponse: try: return await self._gateway_stub.ThrowError( diff --git a/pyzeebe/grpc_internals/zeebe_message_adapter.py b/pyzeebe/grpc_internals/zeebe_message_adapter.py index e20a5e2d..ff6fb278 100644 --- a/pyzeebe/grpc_internals/zeebe_message_adapter.py +++ b/pyzeebe/grpc_internals/zeebe_message_adapter.py @@ -1,5 +1,5 @@ import json -from typing import Dict, Optional +from typing import Optional import grpc from zeebe_grpc.gateway_pb2 import PublishMessageRequest, PublishMessageResponse @@ -7,6 +7,7 @@ from pyzeebe.errors import MessageAlreadyExistsError from pyzeebe.grpc_internals.grpc_utils import is_error_status from pyzeebe.grpc_internals.zeebe_adapter_base import ZeebeAdapterBase +from pyzeebe.types import Variables class ZeebeMessageAdapter(ZeebeAdapterBase): @@ -15,7 +16,7 @@ async def publish_message( name: str, correlation_key: str, time_to_live_in_milliseconds: int, - variables: Dict, + variables: Variables, message_id: Optional[str] = None, tenant_id: Optional[str] = None, ) -> PublishMessageResponse: diff --git a/pyzeebe/grpc_internals/zeebe_process_adapter.py b/pyzeebe/grpc_internals/zeebe_process_adapter.py index bfe8b42c..83631827 100644 --- a/pyzeebe/grpc_internals/zeebe_process_adapter.py +++ b/pyzeebe/grpc_internals/zeebe_process_adapter.py @@ -1,6 +1,6 @@ import json import os -from typing import Dict, Optional, Tuple +from typing import Any, Dict, Iterable, NoReturn, Optional, Tuple, cast import aiofiles import grpc @@ -27,6 +27,7 @@ ) from pyzeebe.grpc_internals.grpc_utils import is_error_status from pyzeebe.grpc_internals.zeebe_adapter_base import ZeebeAdapterBase +from pyzeebe.types import Variables class ZeebeProcessAdapter(ZeebeAdapterBase): @@ -34,7 +35,7 @@ async def create_process_instance( self, bpmn_process_id: str, version: int, - variables: Dict, + variables: Variables, tenant_id: Optional[str] = None, ) -> int: try: @@ -48,17 +49,17 @@ async def create_process_instance( ) except grpc.aio.AioRpcError as grpc_error: await self._create_process_errors(grpc_error, bpmn_process_id, version, variables) - return response.processInstanceKey + return cast(int, response.processInstanceKey) async def create_process_instance_with_result( self, bpmn_process_id: str, version: int, - variables: Dict, + variables: Variables, timeout: int, - variables_to_fetch, + variables_to_fetch: Iterable[str], tenant_id: Optional[str] = None, - ) -> Tuple[int, Dict]: + ) -> Tuple[int, Dict[str, Any]]: try: response = await self._gateway_stub.CreateProcessInstanceWithResult( CreateProcessInstanceWithResultRequest( @@ -77,8 +78,8 @@ async def create_process_instance_with_result( return response.processInstanceKey, json.loads(response.variables) async def _create_process_errors( - self, grpc_error: grpc.aio.AioRpcError, bpmn_process_id: str, version: int, variables: Dict - ) -> None: + self, grpc_error: grpc.aio.AioRpcError, bpmn_process_id: str, version: int, variables: Dict[str, Any] + ) -> NoReturn: if is_error_status(grpc_error, grpc.StatusCode.NOT_FOUND): raise ProcessDefinitionNotFoundError(bpmn_process_id=bpmn_process_id, version=version) from grpc_error elif is_error_status(grpc_error, grpc.StatusCode.INVALID_ARGUMENT): diff --git a/pyzeebe/job/job.py b/pyzeebe/job/job.py index 8410026b..91625882 100644 --- a/pyzeebe/job/job.py +++ b/pyzeebe/job/job.py @@ -1,9 +1,13 @@ import copy from dataclasses import dataclass -from typing import Dict, Optional +from typing import TYPE_CHECKING, Any, Dict, Optional from pyzeebe.errors import NoZeebeAdapterError from pyzeebe.job.job_status import JobStatus +from pyzeebe.types import Variables + +if TYPE_CHECKING: + from pyzeebe.grpc_internals.zeebe_adapter import ZeebeAdapter @dataclass @@ -16,14 +20,14 @@ class Job: process_definition_key: int element_id: str element_instance_key: int - custom_headers: Dict + custom_headers: Dict[str, Any] worker: str retries: int deadline: int - variables: Dict + variables: Variables tenant_id: Optional[str] = None status: JobStatus = JobStatus.Running - zeebe_adapter: Optional["ZeebeAdapter"] = None # type: ignore + zeebe_adapter: Optional["ZeebeAdapter"] = None async def set_running_after_decorators_status(self) -> None: """ @@ -62,7 +66,7 @@ async def set_failure_status( self, message: str, retry_back_off_ms: int = 0, - variables: Optional[Dict] = None, + variables: Optional[Variables] = None, ) -> None: """ Failure status means a technical error has occurred. If retried the job may succeed. @@ -97,7 +101,7 @@ async def set_error_status( self, message: str, error_code: str = "", - variables: Optional[Dict] = None, + variables: Optional[Variables] = None, ) -> None: """ Error status means that the job could not be completed because of a business error and won't ever be able to be completed. diff --git a/pyzeebe/py.typed b/pyzeebe/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/pyzeebe/task/exception_handler.py b/pyzeebe/task/exception_handler.py index 4f762c58..af981564 100644 --- a/pyzeebe/task/exception_handler.py +++ b/pyzeebe/task/exception_handler.py @@ -6,7 +6,7 @@ logger = logging.getLogger(__name__) -ExceptionHandler = Callable[[Exception, Job], Awaitable] +ExceptionHandler = Callable[[Exception, Job], Awaitable[None]] async def default_exception_handler(e: Exception, job: Job) -> None: diff --git a/pyzeebe/task/task.py b/pyzeebe/task/task.py index 09e1eabe..450918fe 100644 --- a/pyzeebe/task/task.py +++ b/pyzeebe/task/task.py @@ -1,20 +1,23 @@ -from typing import Callable +from __future__ import annotations +from typing import Any + +from pyzeebe.function_tools import Function from pyzeebe.task.task_config import TaskConfig from pyzeebe.task.types import JobHandler class Task: - def __init__(self, original_function: Callable, job_handler: JobHandler, config: TaskConfig): + def __init__(self, original_function: Function[..., Any], job_handler: JobHandler, config: TaskConfig) -> None: self.original_function = original_function self.job_handler = job_handler self.config = config @property - def type(self): + def type(self) -> str: return self.config.type - def __repr__(self): + def __repr__(self) -> str: return ( f"Task(config= {self.config}, original_function={self.original_function}, " f"job_handler={self.job_handler})" diff --git a/pyzeebe/task/task_builder.py b/pyzeebe/task/task_builder.py index dceef6ed..49d619e9 100644 --- a/pyzeebe/task/task_builder.py +++ b/pyzeebe/task/task_builder.py @@ -1,6 +1,10 @@ +from __future__ import annotations + import functools import logging -from typing import Dict, Sequence, Tuple +from typing import Any, Dict, Sequence, Tuple, TypeVar + +from typing_extensions import ParamSpec from pyzeebe import Job from pyzeebe.function_tools import DictFunction, Function @@ -13,15 +17,18 @@ from pyzeebe.task.task_config import TaskConfig from pyzeebe.task.types import AsyncTaskDecorator, DecoratorRunner, JobHandler +P = ParamSpec("P") +R = TypeVar("R") + logger = logging.getLogger(__name__) -def build_task(task_function: Function, task_config: TaskConfig) -> Task: +def build_task(task_function: Function[..., Any], task_config: TaskConfig) -> Task: task_config.job_parameter_name = get_job_parameter_name(task_function) return Task(task_function, build_job_handler(task_function, task_config), task_config) -def build_job_handler(task_function: Function, task_config: TaskConfig) -> JobHandler: +def build_job_handler(task_function: Function[..., Any], task_config: TaskConfig) -> JobHandler: prepared_task_function = prepare_task_function(task_function, task_config) before_decorator_runner = create_decorator_runner(task_config.before) @@ -35,7 +42,7 @@ async def job_handler(job: Job) -> Job: job = await before_decorator_runner(job) original_return_value, succeeded = await run_original_task_function(prepared_task_function, task_config, job) job.variables.update(original_return_value) - job.variables.pop(task_config.job_parameter_name, None) + job.variables.pop(task_config.job_parameter_name, None) # type: ignore[arg-type] await job.set_running_after_decorators_status() job = await after_decorator_runner(job) if succeeded: @@ -45,18 +52,19 @@ async def job_handler(job: Job) -> Job: return job_handler -def prepare_task_function(task_function: Function, task_config: TaskConfig) -> DictFunction: +def prepare_task_function(task_function: Function[P, R], task_config: TaskConfig) -> DictFunction[P]: if not is_async_function(task_function): task_function = asyncify(task_function) if task_config.single_value: - task_function = convert_to_dict_function(task_function, task_config.variable_name) - return task_function + return convert_to_dict_function(task_function, task_config.variable_name) + # we check return type in task decorator + return task_function # type: ignore[return-value] async def run_original_task_function( - task_function: DictFunction, task_config: TaskConfig, job: Job -) -> Tuple[Dict, bool]: + task_function: DictFunction[...], task_config: TaskConfig, job: Job +) -> Tuple[Dict[str, Any], bool]: try: if task_config.variables_to_fetch is None: variables = {} @@ -66,7 +74,7 @@ async def run_original_task_function( for k, v in job.variables.items() if k in task_config.variables_to_fetch or k == task_config.job_parameter_name } - returned_value = await task_function(**variables) # type: ignore + returned_value = await task_function(**variables) if returned_value is None: returned_value = {} @@ -80,7 +88,7 @@ async def run_original_task_function( def create_decorator_runner(decorators: Sequence[AsyncTaskDecorator]) -> DecoratorRunner: - async def decorator_runner(job: Job): + async def decorator_runner(job: Job) -> Job: for decorator in decorators: job = await run_decorator(decorator, job) return job diff --git a/pyzeebe/task/task_config.py b/pyzeebe/task/task_config.py index 84c54445..41e3a40e 100644 --- a/pyzeebe/task/task_config.py +++ b/pyzeebe/task/task_config.py @@ -1,4 +1,4 @@ -from typing import List, Optional +from typing import Iterable, List, Optional from pyzeebe.errors import NoVariableNameGivenError from pyzeebe.function_tools import async_tools @@ -17,12 +17,12 @@ def __init__( timeout_ms: int, max_jobs_to_activate: int, max_running_jobs: int, - variables_to_fetch: Optional[List[str]], + variables_to_fetch: Optional[Iterable[str]], single_value: bool, variable_name: str, before: List[TaskDecorator], after: List[TaskDecorator], - ): + ) -> None: if single_value and not variable_name: raise NoVariableNameGivenError(type) @@ -38,7 +38,7 @@ def __init__( self.after = async_tools.asyncify_all_functions(after) self.job_parameter_name: Optional[str] = None - def __repr__(self): + def __repr__(self) -> str: return ( f"TaskConfig(type={self.type}, exception_handler={self.exception_handler}, " f"timeout_ms={self.timeout_ms}, max_jobs_to_activate={self.max_jobs_to_activate}, " diff --git a/pyzeebe/types.py b/pyzeebe/types.py new file mode 100644 index 00000000..66db9039 --- /dev/null +++ b/pyzeebe/types.py @@ -0,0 +1,5 @@ +from typing import Any, Dict + +from typing_extensions import TypeAlias + +Variables: TypeAlias = Dict[str, Any] diff --git a/pyzeebe/worker/job_executor.py b/pyzeebe/worker/job_executor.py index 40959996..27782d96 100644 --- a/pyzeebe/worker/job_executor.py +++ b/pyzeebe/worker/job_executor.py @@ -9,11 +9,11 @@ logger = logging.getLogger(__name__) -AsyncTaskCallback = Callable[[asyncio.Future], None] +AsyncTaskCallback = Callable[["asyncio.Future[None]"], None] class JobExecutor: - def __init__(self, task: Task, jobs: asyncio.Queue, task_state: TaskState): + def __init__(self, task: Task, jobs: "asyncio.Queue[Job]", task_state: TaskState): self.task = task self.jobs = jobs self.task_state = task_state @@ -43,7 +43,7 @@ async def stop(self) -> None: def create_job_callback(job_executor: JobExecutor, job: Job) -> AsyncTaskCallback: - def callback(_): + def callback(_: "asyncio.Future[None]") -> None: job_executor.jobs.task_done() job_executor.task_state.remove(job) diff --git a/pyzeebe/worker/job_poller.py b/pyzeebe/worker/job_poller.py index 1bba63b1..1e044cfb 100644 --- a/pyzeebe/worker/job_poller.py +++ b/pyzeebe/worker/job_poller.py @@ -9,6 +9,7 @@ ZeebeInternalError, ) from pyzeebe.grpc_internals.zeebe_job_adapter import ZeebeJobAdapter +from pyzeebe.job.job import Job from pyzeebe.task.task import Task from pyzeebe.worker.task_state import TaskState @@ -20,13 +21,13 @@ def __init__( self, zeebe_adapter: ZeebeJobAdapter, task: Task, - queue: asyncio.Queue, + queue: "asyncio.Queue[Job]", worker_name: str, request_timeout: int, task_state: TaskState, poll_retry_delay: int, tenant_ids: Optional[List[str]], - ): + ) -> None: self.zeebe_adapter = zeebe_adapter self.task = task self.queue = queue @@ -37,11 +38,11 @@ def __init__( self.tenant_ids = tenant_ids self.stop_event = asyncio.Event() - async def poll(self): + async def poll(self) -> None: while self.should_poll(): await self.activate_max_jobs() - async def activate_max_jobs(self): + async def activate_max_jobs(self) -> None: if self.calculate_max_jobs_to_activate() > 0: await self.poll_once() else: @@ -52,7 +53,7 @@ async def activate_max_jobs(self): ) await asyncio.sleep(self.poll_retry_delay) - async def poll_once(self): + async def poll_once(self) -> None: try: jobs = self.zeebe_adapter.activate_jobs( task_type=self.task.type, @@ -83,6 +84,6 @@ def calculate_max_jobs_to_activate(self) -> int: worker_max_jobs = self.task.config.max_running_jobs - self.task_state.count_active() return min(worker_max_jobs, self.task.config.max_jobs_to_activate) - async def stop(self): + async def stop(self) -> None: self.stop_event.set() await self.queue.join() diff --git a/pyzeebe/worker/task_router.py b/pyzeebe/worker/task_router.py index 16dd4712..39dd9473 100644 --- a/pyzeebe/worker/task_router.py +++ b/pyzeebe/worker/task_router.py @@ -1,14 +1,32 @@ import logging -from typing import Callable, List, Optional, Tuple - -from pyzeebe.errors import DuplicateTaskTypeError, TaskNotFoundError -from pyzeebe.function_tools import parameter_tools +from typing import ( + Any, + Callable, + Dict, + Iterable, + List, + Literal, + Optional, + Tuple, + TypeVar, + overload, +) + +from typing_extensions import ParamSpec + +from pyzeebe.errors import BusinessError, DuplicateTaskTypeError, TaskNotFoundError +from pyzeebe.function_tools import DictFunction, Function, parameter_tools +from pyzeebe.job.job import Job from pyzeebe.task import task_builder from pyzeebe.task.exception_handler import ExceptionHandler from pyzeebe.task.task import Task from pyzeebe.task.task_config import TaskConfig from pyzeebe.task.types import TaskDecorator +P = ParamSpec("P") +R = TypeVar("R") +RD = TypeVar("RD", bound=Optional[Dict[str, Any]]) + logger = logging.getLogger(__name__) @@ -30,11 +48,44 @@ def __init__( self._after: List[TaskDecorator] = after or [] self.tasks: List[Task] = [] + @overload + def task( + self, + task_type: str, + exception_handler: Optional[ExceptionHandler] = None, + variables_to_fetch: Optional[Iterable[str]] = None, + timeout_ms: int = 10000, + max_jobs_to_activate: int = 32, + max_running_jobs: int = 32, + before: Optional[List[TaskDecorator]] = None, + after: Optional[List[TaskDecorator]] = None, + *, + single_value: Literal[False] = False, + ) -> Callable[[Function[P, RD]], Function[P, RD]]: + ... + + @overload def task( self, task_type: str, exception_handler: Optional[ExceptionHandler] = None, - variables_to_fetch: Optional[List[str]] = None, + variables_to_fetch: Optional[Iterable[str]] = None, + timeout_ms: int = 10000, + max_jobs_to_activate: int = 32, + max_running_jobs: int = 32, + before: Optional[List[TaskDecorator]] = None, + after: Optional[List[TaskDecorator]] = None, + *, + single_value: Literal[True], + variable_name: str, + ) -> Callable[[Function[P, R]], Function[P, R]]: + ... + + def task( + self, + task_type: str, + exception_handler: Optional[ExceptionHandler] = None, + variables_to_fetch: Optional[Iterable[str]] = None, timeout_ms: int = 10000, max_jobs_to_activate: int = 32, max_running_jobs: int = 32, @@ -42,14 +93,14 @@ def task( after: Optional[List[TaskDecorator]] = None, single_value: bool = False, variable_name: Optional[str] = None, - ): + ) -> Callable[[Function[P, R]], Function[P, R]]: """ Decorator to create a task Args: task_type (str): The task type exception_handler (ExceptionHandler): Handler that will be called when a job fails. - variables_to_fetch (Optional[List[str]]): The variables to request from Zeebe when activating jobs. + variables_to_fetch (Optional[Iterable[str]]): The variables to request from Zeebe when activating jobs. timeout_ms (int): Maximum duration of the task in milliseconds. If the timeout is surpassed Zeebe will give up on the worker and retry it. Default: 10000 (10 seconds). max_jobs_to_activate (int): Maximum amount of jobs the worker will activate in one request to the Zeebe gateway. Default: 32 @@ -67,7 +118,7 @@ def task( """ _exception_handler = exception_handler or self._exception_handler - def task_wrapper(task_function: Callable): + def task_wrapper(task_function: Function[P, R]) -> Function[P, R]: config = TaskConfig( task_type, _exception_handler, @@ -88,7 +139,7 @@ def task_wrapper(task_function: Callable): return task_wrapper - def _add_task(self, task: Task): + def _add_task(self, task: Task) -> None: self._is_task_duplicate(task.type) self.tasks.append(task) diff --git a/pyzeebe/worker/task_state.py b/pyzeebe/worker/task_state.py index da0a22f3..7b0d2a1a 100644 --- a/pyzeebe/worker/task_state.py +++ b/pyzeebe/worker/task_state.py @@ -1,4 +1,5 @@ import logging +from typing import List from pyzeebe import Job @@ -6,8 +7,8 @@ class TaskState: - def __init__(self): - self._active_jobs = [] + def __init__(self) -> None: + self._active_jobs: List[int] = [] def remove(self, job: Job) -> None: try: diff --git a/pyzeebe/worker/worker.py b/pyzeebe/worker/worker.py index f66f3855..60fc7a9a 100644 --- a/pyzeebe/worker/worker.py +++ b/pyzeebe/worker/worker.py @@ -7,6 +7,7 @@ from pyzeebe import TaskDecorator from pyzeebe.grpc_internals.zeebe_adapter import ZeebeAdapter +from pyzeebe.job.job import Job from pyzeebe.task import task_builder from pyzeebe.task.exception_handler import ExceptionHandler from pyzeebe.worker.job_executor import JobExecutor @@ -54,7 +55,7 @@ def __init__( self._watcher_thread = None self.poll_retry_delay = poll_retry_delay self.tenant_ids = tenant_ids - self._work_task: Optional[asyncio.Future] = None + self._work_task: "Optional[asyncio.Future[List[None]]]" = None self._job_pollers: List[JobPoller] = [] self._job_executors: List[JobExecutor] = [] @@ -73,7 +74,7 @@ async def work(self) -> None: self._job_executors, self._job_pollers = [], [] for task in self.tasks: - jobs_queue: asyncio.Queue = asyncio.Queue() + jobs_queue: "asyncio.Queue[Job]" = asyncio.Queue() task_state = TaskState() poller = JobPoller(