From 1fbe074436f7bd7a4d815625e19862700d0299a6 Mon Sep 17 00:00:00 2001 From: Chad Retz Date: Mon, 17 Jul 2023 09:39:17 -0500 Subject: [PATCH 1/3] Catch BaseException in activities Fixes #333 --- temporalio/worker/_activity.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/temporalio/worker/_activity.py b/temporalio/worker/_activity.py index 5c22a1b1..223fd3d9 100644 --- a/temporalio/worker/_activity.py +++ b/temporalio/worker/_activity.py @@ -436,12 +436,7 @@ async def _run_activity( completion.result.completed.result.CopyFrom( (await self._data_converter.encode([result]))[0] ) - except ( - Exception, - asyncio.CancelledError, - temporalio.exceptions.CancelledError, - temporalio.activity._CompleteAsyncError, - ) as err: + except BaseException as err: try: if isinstance(err, temporalio.activity._CompleteAsyncError): temporalio.activity.logger.debug("Completing asynchronously") From d61100b4fd9fd480df61041ad01efaeea2e8b3ba Mon Sep 17 00:00:00 2001 From: Chad Retz Date: Mon, 17 Jul 2023 10:23:37 -0500 Subject: [PATCH 2/3] Support typing.Self annotation Fixes #318 --- poetry.lock | 68 +++++++++++++++++------------------ temporalio/common.py | 5 ++- tests/worker/test_workflow.py | 26 +++++++++++++- 3 files changed, 63 insertions(+), 36 deletions(-) diff --git a/poetry.lock b/poetry.lock index 6e6ebc31..91a8190e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -868,45 +868,45 @@ files = [ [[package]] name = "mypy" -version = "1.0.0" +version = "1.4.1" description = "Optional static typing for Python" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "mypy-1.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0626db16705ab9f7fa6c249c017c887baf20738ce7f9129da162bb3075fc1af"}, - {file = "mypy-1.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1ace23f6bb4aec4604b86c4843276e8fa548d667dbbd0cb83a3ae14b18b2db6c"}, - {file = "mypy-1.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87edfaf344c9401942883fad030909116aa77b0fa7e6e8e1c5407e14549afe9a"}, - {file = "mypy-1.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0ab090d9240d6b4e99e1fa998c2d0aa5b29fc0fb06bd30e7ad6183c95fa07593"}, - {file = "mypy-1.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:7cc2c01dfc5a3cbddfa6c13f530ef3b95292f926329929001d45e124342cd6b7"}, - {file = "mypy-1.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:14d776869a3e6c89c17eb943100f7868f677703c8a4e00b3803918f86aafbc52"}, - {file = "mypy-1.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bb2782a036d9eb6b5a6efcdda0986774bf798beef86a62da86cb73e2a10b423d"}, - {file = "mypy-1.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cfca124f0ac6707747544c127880893ad72a656e136adc935c8600740b21ff5"}, - {file = "mypy-1.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8845125d0b7c57838a10fd8925b0f5f709d0e08568ce587cc862aacce453e3dd"}, - {file = "mypy-1.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b1b9e1ed40544ef486fa8ac022232ccc57109f379611633ede8e71630d07d2"}, - {file = "mypy-1.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c7cf862aef988b5fbaa17764ad1d21b4831436701c7d2b653156a9497d92c83c"}, - {file = "mypy-1.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cd187d92b6939617f1168a4fe68f68add749902c010e66fe574c165c742ed88"}, - {file = "mypy-1.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4e5175026618c178dfba6188228b845b64131034ab3ba52acaffa8f6c361f805"}, - {file = "mypy-1.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2f6ac8c87e046dc18c7d1d7f6653a66787a4555085b056fe2d599f1f1a2a2d21"}, - {file = "mypy-1.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7306edca1c6f1b5fa0bc9aa645e6ac8393014fa82d0fa180d0ebc990ebe15964"}, - {file = "mypy-1.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3cfad08f16a9c6611e6143485a93de0e1e13f48cfb90bcad7d5fde1c0cec3d36"}, - {file = "mypy-1.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67cced7f15654710386e5c10b96608f1ee3d5c94ca1da5a2aad5889793a824c1"}, - {file = "mypy-1.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a86b794e8a56ada65c573183756eac8ac5b8d3d59daf9d5ebd72ecdbb7867a43"}, - {file = "mypy-1.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:50979d5efff8d4135d9db293c6cb2c42260e70fb010cbc697b1311a4d7a39ddb"}, - {file = "mypy-1.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3ae4c7a99e5153496243146a3baf33b9beff714464ca386b5f62daad601d87af"}, - {file = "mypy-1.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e398652d005a198a7f3c132426b33c6b85d98aa7dc852137a2a3be8890c4072"}, - {file = "mypy-1.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be78077064d016bc1b639c2cbcc5be945b47b4261a4f4b7d8923f6c69c5c9457"}, - {file = "mypy-1.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92024447a339400ea00ac228369cd242e988dd775640755fa4ac0c126e49bb74"}, - {file = "mypy-1.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:fe523fcbd52c05040c7bee370d66fee8373c5972171e4fbc323153433198592d"}, - {file = "mypy-1.0.0-py3-none-any.whl", hash = "sha256:2efa963bdddb27cb4a0d42545cd137a8d2b883bd181bbc4525b568ef6eca258f"}, - {file = "mypy-1.0.0.tar.gz", hash = "sha256:f34495079c8d9da05b183f9f7daec2878280c2ad7cc81da686ef0b484cea2ecf"}, + {file = "mypy-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:566e72b0cd6598503e48ea610e0052d1b8168e60a46e0bfd34b3acf2d57f96a8"}, + {file = "mypy-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca637024ca67ab24a7fd6f65d280572c3794665eaf5edcc7e90a866544076878"}, + {file = "mypy-1.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dde1d180cd84f0624c5dcaaa89c89775550a675aff96b5848de78fb11adabcd"}, + {file = "mypy-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8c4d8e89aa7de683e2056a581ce63c46a0c41e31bd2b6d34144e2c80f5ea53dc"}, + {file = "mypy-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:bfdca17c36ae01a21274a3c387a63aa1aafe72bff976522886869ef131b937f1"}, + {file = "mypy-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7549fbf655e5825d787bbc9ecf6028731973f78088fbca3a1f4145c39ef09462"}, + {file = "mypy-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:98324ec3ecf12296e6422939e54763faedbfcc502ea4a4c38502082711867258"}, + {file = "mypy-1.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:141dedfdbfe8a04142881ff30ce6e6653c9685b354876b12e4fe6c78598b45e2"}, + {file = "mypy-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8207b7105829eca6f3d774f64a904190bb2231de91b8b186d21ffd98005f14a7"}, + {file = "mypy-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:16f0db5b641ba159eff72cff08edc3875f2b62b2fa2bc24f68c1e7a4e8232d01"}, + {file = "mypy-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:470c969bb3f9a9efcedbadcd19a74ffb34a25f8e6b0e02dae7c0e71f8372f97b"}, + {file = "mypy-1.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5952d2d18b79f7dc25e62e014fe5a23eb1a3d2bc66318df8988a01b1a037c5b"}, + {file = "mypy-1.4.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:190b6bab0302cec4e9e6767d3eb66085aef2a1cc98fe04936d8a42ed2ba77bb7"}, + {file = "mypy-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9d40652cc4fe33871ad3338581dca3297ff5f2213d0df345bcfbde5162abf0c9"}, + {file = "mypy-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:01fd2e9f85622d981fd9063bfaef1aed6e336eaacca00892cd2d82801ab7c042"}, + {file = "mypy-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2460a58faeea905aeb1b9b36f5065f2dc9a9c6e4c992a6499a2360c6c74ceca3"}, + {file = "mypy-1.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2746d69a8196698146a3dbe29104f9eb6a2a4d8a27878d92169a6c0b74435b6"}, + {file = "mypy-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ae704dcfaa180ff7c4cfbad23e74321a2b774f92ca77fd94ce1049175a21c97f"}, + {file = "mypy-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:43d24f6437925ce50139a310a64b2ab048cb2d3694c84c71c3f2a1626d8101dc"}, + {file = "mypy-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c482e1246726616088532b5e964e39765b6d1520791348e6c9dc3af25b233828"}, + {file = "mypy-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:43b592511672017f5b1a483527fd2684347fdffc041c9ef53428c8dc530f79a3"}, + {file = "mypy-1.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34a9239d5b3502c17f07fd7c0b2ae6b7dd7d7f6af35fbb5072c6208e76295816"}, + {file = "mypy-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5703097c4936bbb9e9bce41478c8d08edd2865e177dc4c52be759f81ee4dd26c"}, + {file = "mypy-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e02d700ec8d9b1859790c0475df4e4092c7bf3272a4fd2c9f33d87fac4427b8f"}, + {file = "mypy-1.4.1-py3-none-any.whl", hash = "sha256:45d32cec14e7b97af848bddd97d85ea4f0db4d5a149ed9676caa4eb2f7402bb4"}, + {file = "mypy-1.4.1.tar.gz", hash = "sha256:9bbcd9ab8ea1f2e1c8031c21445b511442cc45c89951e49bbf852cbb70755b1b"}, ] [package.dependencies] -mypy-extensions = ">=0.4.3" +mypy-extensions = ">=1.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typed-ast = {version = ">=1.4.0,<2", markers = "python_version < \"3.8\""} -typing-extensions = ">=3.10" +typing-extensions = ">=4.1.0" [package.extras] dmypy = ["psutil (>=4.0)"] @@ -916,14 +916,14 @@ reports = ["lxml"] [[package]] name = "mypy-extensions" -version = "0.4.3" -description = "Experimental type system extensions for programs checked with the mypy typechecker." +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.5" files = [ - {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, - {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] [[package]] diff --git a/temporalio/common.py b/temporalio/common.py index 02e44d17..c31c84bf 100644 --- a/temporalio/common.py +++ b/temporalio/common.py @@ -260,7 +260,10 @@ def _type_hints_from_func( if ( index == 0 and value.name == "self" - and value.annotation is inspect.Parameter.empty + and ( + value.annotation is inspect.Parameter.empty + or str(value.annotation) == "typing.Self" + ) ): continue # Stop if non-positional or not a class diff --git a/tests/worker/test_workflow.py b/tests/worker/test_workflow.py index fa05acfd..21526823 100644 --- a/tests/worker/test_workflow.py +++ b/tests/worker/test_workflow.py @@ -7,8 +7,8 @@ import queue import sys import threading +import typing import uuid -import warnings from abc import ABC, abstractmethod from dataclasses import dataclass from datetime import datetime, timedelta, timezone @@ -3055,3 +3055,27 @@ async def test_workflow_dynamic(client: Client): ) assert isinstance(result, DynamicWorkflowValue) assert result == DynamicWorkflowValue("some-workflow - val1 - val2") + + +# typing.Self only in 3.11+ +if sys.version_info >= (3, 11): + + @dataclass + class AnnotatedWithSelfParam: + some_str: str + + @workflow.defn + class WorkflowAnnotatedWithSelf: + @workflow.run + async def run(self: typing.Self, some_arg: AnnotatedWithSelfParam) -> str: + assert isinstance(some_arg, AnnotatedWithSelfParam) + return some_arg.some_str + + async def test_workflow_annotated_with_self(client: Client): + async with new_worker(client, WorkflowAnnotatedWithSelf) as worker: + assert "foo" == await client.execute_workflow( + WorkflowAnnotatedWithSelf.run, + AnnotatedWithSelfParam(some_str="foo"), + id=f"wf-{uuid.uuid4()}", + task_queue=worker.task_queue, + ) From f25ffd3534da4f986c31a45cc6519942be6d270a Mon Sep 17 00:00:00 2001 From: Chad Retz Date: Mon, 17 Jul 2023 13:07:18 -0500 Subject: [PATCH 3/3] Log and drop signals whose params can't be deserialized Fixes #347 --- temporalio/worker/_workflow_instance.py | 16 ++++++++-- tests/worker/test_workflow.py | 40 +++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/temporalio/worker/_workflow_instance.py b/temporalio/worker/_workflow_instance.py index a5265cc3..8d155581 100644 --- a/temporalio/worker/_workflow_instance.py +++ b/temporalio/worker/_workflow_instance.py @@ -1231,9 +1231,19 @@ def _process_signal_job( defn: temporalio.workflow._SignalDefinition, job: temporalio.bridge.proto.workflow_activation.SignalWorkflow, ) -> None: - args = self._process_handler_args( - job.signal_name, job.input, defn.name, defn.arg_types, defn.dynamic_vararg - ) + try: + args = self._process_handler_args( + job.signal_name, + job.input, + defn.name, + defn.arg_types, + defn.dynamic_vararg, + ) + except Exception: + logger.exception( + f"Failed deserializing signal input for {job.signal_name}, dropping the signal" + ) + return input = HandleSignalInput( signal=job.signal_name, args=args, headers=job.headers ) diff --git a/tests/worker/test_workflow.py b/tests/worker/test_workflow.py index 21526823..6b6f0a27 100644 --- a/tests/worker/test_workflow.py +++ b/tests/worker/test_workflow.py @@ -493,6 +493,46 @@ async def test_workflow_signal_qnd_query_handlers_old_dynamic_style(client: Clie ) +@dataclass +class BadSignalParam: + some_str: str + + +@workflow.defn +class BadSignalParamWorkflow: + def __init__(self) -> None: + self._signals: List[BadSignalParam] = [] + + @workflow.run + async def run(self) -> List[BadSignalParam]: + await workflow.wait_condition( + lambda: bool(self._signals) and self._signals[-1].some_str == "finish" + ) + return self._signals + + @workflow.signal + async def some_signal(self, param: BadSignalParam) -> None: + self._signals.append(param) + + +async def test_workflow_bad_signal_param(client: Client): + async with new_worker(client, BadSignalParamWorkflow) as worker: + handle = await client.start_workflow( + BadSignalParamWorkflow.run, + id=f"workflow-{uuid.uuid4()}", + task_queue=worker.task_queue, + ) + # Send 4 signals, first and third are bad + await handle.signal("some_signal", "bad") + await handle.signal("some_signal", BadSignalParam(some_str="good")) + await handle.signal("some_signal", 123) + await handle.signal("some_signal", BadSignalParam(some_str="finish")) + assert [ + BadSignalParam(some_str="good"), + BadSignalParam(some_str="finish"), + ] == await handle.result() + + @workflow.defn class AsyncUtilWorkflow: def __init__(self) -> None: