diff --git a/.gitignore b/.gitignore index 0a3ededf..79c26177 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ temporalio/bridge/target/ temporalio/bridge/temporal_sdk_bridge* /tests/helpers/golangserver/golangserver /tests/helpers/golangworker/golangworker +/.idea diff --git a/README.md b/README.md index bc311e34..55540e11 100644 --- a/README.md +++ b/README.md @@ -458,8 +458,8 @@ Some things to note about the above code: #### Definition Workflows are defined as classes decorated with `@workflow.defn`. The method invoked for the workflow is decorated with -`@workflow.run`. Methods for signals and queries are decorated with `@workflow.signal` and `@workflow.query` -respectively. Here's an example of a workflow: +`@workflow.run`. Methods for signals, queries, and updates are decorated with `@workflow.signal`, `@workflow.query` +and `@workflow.update` respectively. Here's an example of a workflow: ```python import asyncio @@ -515,6 +515,12 @@ class GreetingWorkflow: @workflow.query def current_greeting(self) -> str: return self._current_greeting + + @workflow.update + def set_and_get_greeting(self, greeting: str) -> str: + old = self._current_greeting + self._current_greeting = greeting + return old ``` @@ -582,6 +588,14 @@ Here are the decorators that can be applied: * All the same constraints as `@workflow.signal` but should return a value * Should not be `async` * Temporal queries should never mutate anything in the workflow or call any calls that would mutate the workflow +* `@workflow.update` - Defines a method as an update + * May both accept as input and return a value + * May be `async` or non-`async` + * May mutate workflow state, and make calls to other workflow APIs like starting activities, etc. + * Also accepts the `name` and `dynamic` parameters like signals and queries, with the same semantics. + * Update handlers may optionally define a validator method by decorating it with `@update_handler_method.validator`. + To reject an update before any events are written to history, throw an exception in a validator. Validators cannot + be `async`, cannot mutate workflow state, and return nothing. #### Running @@ -1440,6 +1454,13 @@ to `1` prior to running tests. Do not commit `poetry.lock` or `pyproject.toml` changes. To go back from this downgrade, restore `pyproject.toml` and run `poetry update protobuf grpcio-tools`. +For a less system-intrusive approach, you can: +```shell +docker build -f scripts/_proto/Dockerfile . +docker run -v "${PWD}/temporalio/api:/api_new" -v "${PWD}/temporalio/bridge/proto:/bridge_new" +poe format +``` + ### Style * Mostly [Google Style Guide](https://google.github.io/styleguide/pyguide.html). Notable exceptions: diff --git a/scripts/_proto/Dockerfile b/scripts/_proto/Dockerfile new file mode 100644 index 00000000..ead17b0f --- /dev/null +++ b/scripts/_proto/Dockerfile @@ -0,0 +1,13 @@ +FROM python:3.10 + +RUN python -m pip install --upgrade wheel "poetry==1.3.2" poethepoet +VOLUME ["/api_new", "/bridge_new"] + +COPY ./ ./ + +RUN mkdir -p ./temporalio/api +RUN poetry install --no-root -E opentelemetry +RUN poetry add "protobuf<4" +RUN poe gen-protos + +CMD cp -r ./temporalio/api/* /api_new && cp -r ./temporalio/bridge/proto/* /bridge_new diff --git a/scripts/_proto/Dockerfile.dockerignore b/scripts/_proto/Dockerfile.dockerignore new file mode 100644 index 00000000..93d7c20a --- /dev/null +++ b/scripts/_proto/Dockerfile.dockerignore @@ -0,0 +1,11 @@ +.git/ +.idea/ +.mypy_cache/ +.pytest_cache/ +.venv/ +build/ +dist/ +temporalio/api/ +temporalio/bridge/**/target/ +temporalio/bridge/**/*.so +Dockerfile \ No newline at end of file diff --git a/temporalio/bridge/Cargo.lock b/temporalio/bridge/Cargo.lock index 4fa15f8f..4154e028 100644 --- a/temporalio/bridge/Cargo.lock +++ b/temporalio/bridge/Cargo.lock @@ -30,20 +30,21 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +checksum = "cd7d5a2cecb58716e47d67d5703a249964b14c7be1ec3cad3affc295b2d1c35d" dependencies = [ "cfg-if", "once_cell", "version_check", + "zerocopy", ] [[package]] name = "aho-corasick" -version = "1.0.4" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] @@ -54,12 +55,6 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - [[package]] name = "anyhow" version = "1.0.75" @@ -91,18 +86,18 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] name = "async-trait" -version = "0.1.73" +version = "0.1.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -184,9 +179,9 @@ dependencies = [ [[package]] name = "base64" -version = "0.21.2" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" [[package]] name = "base64ct" @@ -202,9 +197,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "block-buffer" @@ -217,21 +212,21 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "bzip2" @@ -272,11 +267,10 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.26" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ - "android-tzdata", "num-traits", "serde", ] @@ -321,9 +315,9 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "3fbc60abd742b35f2492f808e1abbb83d45f72db402e14c55057edc9c7b1e9e4" dependencies = [ "libc", ] @@ -451,12 +445,12 @@ dependencies = [ [[package]] name = "dashmap" -version = "5.5.1" +version = "5.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd72493923899c6f10c641bdbdeddc7183d6396641d99c1a0d1597f37f92e28" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown 0.14.0", + "hashbrown 0.14.2", "lock_api", "once_cell", "parking_lot_core", @@ -464,9 +458,12 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" +checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +dependencies = [ + "powerfmt", +] [[package]] name = "derive_builder" @@ -567,7 +564,7 @@ checksum = "eecf8589574ce9b895052fa12d69af7a233f99e6107f5cb8dd1044f2a17bfdcb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -579,7 +576,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -590,39 +587,28 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "erased-serde" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "837c0466252947ada828b975e12daf82e18bb5444e4df87be6038d4469e2a3d2" +checksum = "6c138974f9d5e7fe373eb04df7cae98833802ae4b11c24ac7039a21d5af4b26c" dependencies = [ "serde", ] [[package]] name = "errno" -version = "0.3.2" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" dependencies = [ - "errno-dragonfly", "libc", "windows-sys", ] -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "fastrand" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "filetime" @@ -632,7 +618,7 @@ checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.3.5", "windows-sys", ] @@ -644,9 +630,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", "miniz_oxide", @@ -738,7 +724,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -860,9 +846,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" dependencies = [ "ahash", "allocator-api2", @@ -876,9 +862,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "hmac" @@ -889,6 +875,15 @@ dependencies = [ "digest", ] +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys", +] + [[package]] name = "http" version = "0.2.9" @@ -940,7 +935,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.9", + "socket2 0.4.10", "tokio", "tower-service", "tracing", @@ -1001,12 +996,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.0" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown 0.14.2", ] [[package]] @@ -1035,15 +1030,15 @@ dependencies = [ [[package]] name = "inventory" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a53088c87cf71c9d4f3372a2cb9eea1e7b8a0b1bf8b7f7d23fe5b76dbb07e63b" +checksum = "e1be380c410bf0595e94992a648ea89db4dd3f3354ba54af206fd2a68cf5ac8e" [[package]] name = "ipnet" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "itertools" @@ -1071,9 +1066,9 @@ checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "jobserver" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" dependencies = [ "libc", ] @@ -1095,21 +1090,21 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "linux-raw-sys" -version = "0.4.5" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -1123,11 +1118,11 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "lru" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eedb2bdbad7e0634f83989bf596f497b070130daaa398ab22d84c39e266deec5" +checksum = "a4a83fb7698b3643a0e34f9ae6f2e8f0178c0fd42f8b59d493aa271ff3a5bf21" dependencies = [ - "hashbrown 0.14.0", + "hashbrown 0.14.2", ] [[package]] @@ -1150,15 +1145,15 @@ dependencies = [ [[package]] name = "matchit" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed1202b2a6f884ae56f04cff409ab315c5ce26b5e58d7412e484f01fd52f52ef" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "memoffset" @@ -1195,9 +1190,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" dependencies = [ "libc", "wasi", @@ -1243,7 +1238,7 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "cfg-if", "libc", ] @@ -1278,9 +1273,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] @@ -1297,9 +1292,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" dependencies = [ "memchr", ] @@ -1420,9 +1415,9 @@ dependencies = [ [[package]] name = "ordered-float" -version = "3.9.1" +version = "3.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a54938017eacd63036332b4ae5c8a49fc8c0c1d6d629893057e4f13609edd06" +checksum = "f1e1c390732d15f1d48471625cd92d154e66db2c56645e29a9cd26f4699f72dc" dependencies = [ "num-traits", ] @@ -1445,13 +1440,13 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.4.1", "smallvec", "windows-targets", ] @@ -1492,7 +1487,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 2.0.0", + "indexmap 2.0.2", ] [[package]] @@ -1512,14 +1507,14 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] name = "pin-project-lite" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -1533,6 +1528,12 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1581,9 +1582,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -1855,16 +1856,25 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "regex" -version = "1.9.3" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.3.6", - "regex-syntax 0.7.4", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", ] [[package]] @@ -1878,13 +1888,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.6" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.4", + "regex-syntax 0.8.2", ] [[package]] @@ -1895,15 +1905,15 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.4" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.20" +version = "0.11.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" dependencies = [ "base64", "bytes", @@ -1927,6 +1937,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "system-configuration", "tokio", "tokio-rustls", "tokio-util", @@ -1942,17 +1953,16 @@ dependencies = [ [[package]] name = "ring" -version = "0.16.20" +version = "0.17.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b" dependencies = [ "cc", + "getrandom", "libc", - "once_cell", "spin", "untrusted", - "web-sys", - "winapi", + "windows-sys", ] [[package]] @@ -1995,7 +2005,7 @@ dependencies = [ "proc-macro2", "quote", "rustfsm_trait", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -2004,11 +2014,11 @@ version = "0.1.0" [[package]] name = "rustix" -version = "0.38.8" +version = "0.38.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" +checksum = "67ce50cb2e16c2903e30d1cbccfd8387a74b9d4c938b6a4c5ec6cc7556f7a8a0" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", @@ -2017,9 +2027,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.6" +version = "0.21.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1feddffcfcc0b33f5c6ce9a29e341e4cd59c3f78e7ee45f4a40c038b1d6cbb" +checksum = "446e14c5cda4f3f30fe71863c34ec70f5ac79d6087097ad0bb433e1be5edf04c" dependencies = [ "log", "ring", @@ -2050,9 +2060,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.101.4" +version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d93931baf2d282fff8d3a532bbfd7653f734643161b87e3e01e59a04439bf0d" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ "ring", "untrusted", @@ -2087,9 +2097,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ "ring", "untrusted", @@ -2120,35 +2130,35 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.186" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f5db24220c009de9bd45e69fb2938f4b6d2df856aa9304ce377b3180f83b7c1" +checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.186" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad697f7e0b65af4983a4ce8f56ed5b357e8d3c36651bf6a7e13639c17b8e670" +checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] name = "serde_json" -version = "1.0.105" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", @@ -2169,9 +2179,9 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", @@ -2180,9 +2190,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.7" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", @@ -2191,9 +2201,9 @@ dependencies = [ [[package]] name = "sharded-slab" -version = "0.1.4" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] @@ -2233,15 +2243,15 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "socket2" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ "libc", "winapi", @@ -2249,9 +2259,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.3" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", "windows-sys", @@ -2259,9 +2269,9 @@ dependencies = [ [[package]] name = "spin" -version = "0.5.2" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "strsim" @@ -2288,9 +2298,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.29" +version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ "proc-macro2", "quote", @@ -2303,6 +2313,27 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tar" version = "0.4.40" @@ -2316,9 +2347,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.11" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" +checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" [[package]] name = "tempfile" @@ -2328,7 +2359,7 @@ checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", + "redox_syscall 0.3.5", "rustix", "windows-sys", ] @@ -2404,6 +2435,7 @@ dependencies = [ "hyper", "itertools 0.11.0", "lazy_static", + "log", "lru", "mockall", "nix", @@ -2490,22 +2522,22 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.47" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.47" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -2520,20 +2552,21 @@ dependencies = [ [[package]] name = "time" -version = "0.3.27" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb39ee79a6d8de55f48f2293a830e040392f1c5f16e336bdd1788cd0aadce07" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" dependencies = [ "deranged", + "powerfmt", "serde", "time-core", ] [[package]] name = "time-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "tinyvec" @@ -2552,9 +2585,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.32.0" +version = "1.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" dependencies = [ "backtrace", "bytes", @@ -2564,7 +2597,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.3", + "socket2 0.5.5", "tokio-macros", "windows-sys", ] @@ -2587,7 +2620,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -2613,9 +2646,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" dependencies = [ "bytes", "futures-core", @@ -2704,11 +2737,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "log", "pin-project-lite", "tracing-attributes", @@ -2717,20 +2749,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", "valuable", @@ -2748,12 +2780,12 @@ dependencies = [ [[package]] name = "tracing-log" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" dependencies = [ - "lazy_static", "log", + "once_cell", "tracing-core", ] @@ -2784,9 +2816,9 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "typetag" @@ -2809,7 +2841,7 @@ checksum = "bfc13d450dc4a695200da3074dacf43d449b968baee95e341920e47f61a3b40f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", ] [[package]] @@ -2820,9 +2852,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" @@ -2841,15 +2873,15 @@ checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c" [[package]] name = "untrusted" -version = "0.7.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" dependencies = [ "form_urlencoded", "idna", @@ -2864,9 +2896,9 @@ checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] name = "uuid" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" dependencies = [ "getrandom", ] @@ -2919,7 +2951,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", "wasm-bindgen-shared", ] @@ -2953,7 +2985,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.29", + "syn 2.0.38", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2995,13 +3027,14 @@ checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" [[package]] name = "which" -version = "4.4.0" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" dependencies = [ "either", - "libc", + "home", "once_cell", + "rustix", ] [[package]] @@ -3111,6 +3144,26 @@ dependencies = [ "libc", ] +[[package]] +name = "zerocopy" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c19fae0c8a9efc6a8281f2e623db8af1db9e57852e04cde3e754dd2dc29340f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc56589e9ddd1f1c28d4b4b5c773ce232910a6bb67a70133d61c9e347585efe9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "zip" version = "0.6.6" @@ -3152,11 +3205,10 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.8+zstd.1.5.5" +version = "2.0.9+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" dependencies = [ "cc", - "libc", "pkg-config", ] diff --git a/temporalio/bridge/proto/workflow_activation/__init__.py b/temporalio/bridge/proto/workflow_activation/__init__.py index a44f6415..ce302730 100644 --- a/temporalio/bridge/proto/workflow_activation/__init__.py +++ b/temporalio/bridge/proto/workflow_activation/__init__.py @@ -1,5 +1,6 @@ from .workflow_activation_pb2 import ( CancelWorkflow, + DoUpdate, FireTimer, NotifyHasPatch, QueryWorkflow, @@ -21,6 +22,7 @@ __all__ = [ "CancelWorkflow", + "DoUpdate", "FireTimer", "NotifyHasPatch", "QueryWorkflow", diff --git a/temporalio/bridge/proto/workflow_activation/workflow_activation_pb2.py b/temporalio/bridge/proto/workflow_activation/workflow_activation_pb2.py index 92720b70..a2a141f0 100644 --- a/temporalio/bridge/proto/workflow_activation/workflow_activation_pb2.py +++ b/temporalio/bridge/proto/workflow_activation/workflow_activation_pb2.py @@ -25,6 +25,9 @@ from temporalio.api.failure.v1 import ( message_pb2 as temporal_dot_api_dot_failure_dot_v1_dot_message__pb2, ) +from temporalio.api.update.v1 import ( + message_pb2 as temporal_dot_api_dot_update_dot_v1_dot_message__pb2, +) from temporalio.bridge.proto.activity_result import ( activity_result_pb2 as temporal_dot_sdk_dot_core_dot_activity__result_dot_activity__result__pb2, ) @@ -36,7 +39,7 @@ ) DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n?temporal/sdk/core/workflow_activation/workflow_activation.proto\x12\x1b\x63oresdk.workflow_activation\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1egoogle/protobuf/duration.proto\x1a%temporal/api/failure/v1/message.proto\x1a$temporal/api/common/v1/message.proto\x1a$temporal/api/enums/v1/workflow.proto\x1a\x37temporal/sdk/core/activity_result/activity_result.proto\x1a\x35temporal/sdk/core/child_workflow/child_workflow.proto\x1a%temporal/sdk/core/common/common.proto"\xa4\x02\n\x12WorkflowActivation\x12\x0e\n\x06run_id\x18\x01 \x01(\t\x12-\n\ttimestamp\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x14\n\x0cis_replaying\x18\x03 \x01(\x08\x12\x16\n\x0ehistory_length\x18\x04 \x01(\r\x12@\n\x04jobs\x18\x05 \x03(\x0b\x32\x32.coresdk.workflow_activation.WorkflowActivationJob\x12 \n\x18\x61vailable_internal_flags\x18\x06 \x03(\r\x12\x1a\n\x12history_size_bytes\x18\x07 \x01(\x04\x12!\n\x19\x63ontinue_as_new_suggested\x18\x08 \x01(\x08"\xe1\x08\n\x15WorkflowActivationJob\x12\x44\n\x0estart_workflow\x18\x01 \x01(\x0b\x32*.coresdk.workflow_activation.StartWorkflowH\x00\x12<\n\nfire_timer\x18\x02 \x01(\x0b\x32&.coresdk.workflow_activation.FireTimerH\x00\x12K\n\x12update_random_seed\x18\x04 \x01(\x0b\x32-.coresdk.workflow_activation.UpdateRandomSeedH\x00\x12\x44\n\x0equery_workflow\x18\x05 \x01(\x0b\x32*.coresdk.workflow_activation.QueryWorkflowH\x00\x12\x46\n\x0f\x63\x61ncel_workflow\x18\x06 \x01(\x0b\x32+.coresdk.workflow_activation.CancelWorkflowH\x00\x12\x46\n\x0fsignal_workflow\x18\x07 \x01(\x0b\x32+.coresdk.workflow_activation.SignalWorkflowH\x00\x12H\n\x10resolve_activity\x18\x08 \x01(\x0b\x32,.coresdk.workflow_activation.ResolveActivityH\x00\x12G\n\x10notify_has_patch\x18\t \x01(\x0b\x32+.coresdk.workflow_activation.NotifyHasPatchH\x00\x12q\n&resolve_child_workflow_execution_start\x18\n \x01(\x0b\x32?.coresdk.workflow_activation.ResolveChildWorkflowExecutionStartH\x00\x12\x66\n resolve_child_workflow_execution\x18\x0b \x01(\x0b\x32:.coresdk.workflow_activation.ResolveChildWorkflowExecutionH\x00\x12\x66\n resolve_signal_external_workflow\x18\x0c \x01(\x0b\x32:.coresdk.workflow_activation.ResolveSignalExternalWorkflowH\x00\x12u\n(resolve_request_cancel_external_workflow\x18\r \x01(\x0b\x32\x41.coresdk.workflow_activation.ResolveRequestCancelExternalWorkflowH\x00\x12I\n\x11remove_from_cache\x18\x32 \x01(\x0b\x32,.coresdk.workflow_activation.RemoveFromCacheH\x00\x42\t\n\x07variant"\xd9\t\n\rStartWorkflow\x12\x15\n\rworkflow_type\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x32\n\targuments\x18\x03 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12\x17\n\x0frandomness_seed\x18\x04 \x01(\x04\x12H\n\x07headers\x18\x05 \x03(\x0b\x32\x37.coresdk.workflow_activation.StartWorkflow.HeadersEntry\x12\x10\n\x08identity\x18\x06 \x01(\t\x12I\n\x14parent_workflow_info\x18\x07 \x01(\x0b\x32+.coresdk.common.NamespacedWorkflowExecution\x12=\n\x1aworkflow_execution_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x37\n\x14workflow_run_timeout\x18\t \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x38\n\x15workflow_task_timeout\x18\n \x01(\x0b\x32\x19.google.protobuf.Duration\x12\'\n\x1f\x63ontinued_from_execution_run_id\x18\x0b \x01(\t\x12J\n\x13\x63ontinued_initiator\x18\x0c \x01(\x0e\x32-.temporal.api.enums.v1.ContinueAsNewInitiator\x12;\n\x11\x63ontinued_failure\x18\r \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12@\n\x16last_completion_result\x18\x0e \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x1e\n\x16\x66irst_execution_run_id\x18\x0f \x01(\t\x12\x39\n\x0cretry_policy\x18\x10 \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12\x0f\n\x07\x61ttempt\x18\x11 \x01(\x05\x12\x15\n\rcron_schedule\x18\x12 \x01(\t\x12\x46\n"workflow_execution_expiration_time\x18\x13 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x45\n"cron_schedule_to_schedule_interval\x18\x14 \x01(\x0b\x32\x19.google.protobuf.Duration\x12*\n\x04memo\x18\x15 \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo\x12\x43\n\x11search_attributes\x18\x16 \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12.\n\nstart_time\x18\x17 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x1aO\n\x0cHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01"\x18\n\tFireTimer\x12\x0b\n\x03seq\x18\x01 \x01(\r"[\n\x0fResolveActivity\x12\x0b\n\x03seq\x18\x01 \x01(\r\x12;\n\x06result\x18\x02 \x01(\x0b\x32+.coresdk.activity_result.ActivityResolution"\xd1\x02\n"ResolveChildWorkflowExecutionStart\x12\x0b\n\x03seq\x18\x01 \x01(\r\x12[\n\tsucceeded\x18\x02 \x01(\x0b\x32\x46.coresdk.workflow_activation.ResolveChildWorkflowExecutionStartSuccessH\x00\x12X\n\x06\x66\x61iled\x18\x03 \x01(\x0b\x32\x46.coresdk.workflow_activation.ResolveChildWorkflowExecutionStartFailureH\x00\x12]\n\tcancelled\x18\x04 \x01(\x0b\x32H.coresdk.workflow_activation.ResolveChildWorkflowExecutionStartCancelledH\x00\x42\x08\n\x06status";\n)ResolveChildWorkflowExecutionStartSuccess\x12\x0e\n\x06run_id\x18\x01 \x01(\t"\xa6\x01\n)ResolveChildWorkflowExecutionStartFailure\x12\x13\n\x0bworkflow_id\x18\x01 \x01(\t\x12\x15\n\rworkflow_type\x18\x02 \x01(\t\x12M\n\x05\x63\x61use\x18\x03 \x01(\x0e\x32>.coresdk.child_workflow.StartChildWorkflowExecutionFailedCause"`\n+ResolveChildWorkflowExecutionStartCancelled\x12\x31\n\x07\x66\x61ilure\x18\x01 \x01(\x0b\x32 .temporal.api.failure.v1.Failure"i\n\x1dResolveChildWorkflowExecution\x12\x0b\n\x03seq\x18\x01 \x01(\r\x12;\n\x06result\x18\x02 \x01(\x0b\x32+.coresdk.child_workflow.ChildWorkflowResult"+\n\x10UpdateRandomSeed\x12\x17\n\x0frandomness_seed\x18\x01 \x01(\x04"\x84\x02\n\rQueryWorkflow\x12\x10\n\x08query_id\x18\x01 \x01(\t\x12\x12\n\nquery_type\x18\x02 \x01(\t\x12\x32\n\targuments\x18\x03 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12H\n\x07headers\x18\x05 \x03(\x0b\x32\x37.coresdk.workflow_activation.QueryWorkflow.HeadersEntry\x1aO\n\x0cHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01"B\n\x0e\x43\x61ncelWorkflow\x12\x30\n\x07\x64\x65tails\x18\x01 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload"\x83\x02\n\x0eSignalWorkflow\x12\x13\n\x0bsignal_name\x18\x01 \x01(\t\x12.\n\x05input\x18\x02 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12\x10\n\x08identity\x18\x03 \x01(\t\x12I\n\x07headers\x18\x05 \x03(\x0b\x32\x38.coresdk.workflow_activation.SignalWorkflow.HeadersEntry\x1aO\n\x0cHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01""\n\x0eNotifyHasPatch\x12\x10\n\x08patch_id\x18\x01 \x01(\t"_\n\x1dResolveSignalExternalWorkflow\x12\x0b\n\x03seq\x18\x01 \x01(\r\x12\x31\n\x07\x66\x61ilure\x18\x02 \x01(\x0b\x32 .temporal.api.failure.v1.Failure"f\n$ResolveRequestCancelExternalWorkflow\x12\x0b\n\x03seq\x18\x01 \x01(\r\x12\x31\n\x07\x66\x61ilure\x18\x02 \x01(\x0b\x32 .temporal.api.failure.v1.Failure"\xc1\x02\n\x0fRemoveFromCache\x12\x0f\n\x07message\x18\x01 \x01(\t\x12K\n\x06reason\x18\x02 \x01(\x0e\x32;.coresdk.workflow_activation.RemoveFromCache.EvictionReason"\xcf\x01\n\x0e\x45victionReason\x12\x0f\n\x0bUNSPECIFIED\x10\x00\x12\x0e\n\nCACHE_FULL\x10\x01\x12\x0e\n\nCACHE_MISS\x10\x02\x12\x12\n\x0eNONDETERMINISM\x10\x03\x12\r\n\tLANG_FAIL\x10\x04\x12\x12\n\x0eLANG_REQUESTED\x10\x05\x12\x12\n\x0eTASK_NOT_FOUND\x10\x06\x12\x15\n\x11UNHANDLED_COMMAND\x10\x07\x12\t\n\x05\x46\x41TAL\x10\x08\x12\x1f\n\x1bPAGINATION_OR_HISTORY_FETCH\x10\tB.\xea\x02+Temporalio::Bridge::Api::WorkflowActivationb\x06proto3' + b'\n?temporal/sdk/core/workflow_activation/workflow_activation.proto\x12\x1b\x63oresdk.workflow_activation\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1egoogle/protobuf/duration.proto\x1a%temporal/api/failure/v1/message.proto\x1a$temporal/api/update/v1/message.proto\x1a$temporal/api/common/v1/message.proto\x1a$temporal/api/enums/v1/workflow.proto\x1a\x37temporal/sdk/core/activity_result/activity_result.proto\x1a\x35temporal/sdk/core/child_workflow/child_workflow.proto\x1a%temporal/sdk/core/common/common.proto"\xa4\x02\n\x12WorkflowActivation\x12\x0e\n\x06run_id\x18\x01 \x01(\t\x12-\n\ttimestamp\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x14\n\x0cis_replaying\x18\x03 \x01(\x08\x12\x16\n\x0ehistory_length\x18\x04 \x01(\r\x12@\n\x04jobs\x18\x05 \x03(\x0b\x32\x32.coresdk.workflow_activation.WorkflowActivationJob\x12 \n\x18\x61vailable_internal_flags\x18\x06 \x03(\r\x12\x1a\n\x12history_size_bytes\x18\x07 \x01(\x04\x12!\n\x19\x63ontinue_as_new_suggested\x18\x08 \x01(\x08"\x9d\t\n\x15WorkflowActivationJob\x12\x44\n\x0estart_workflow\x18\x01 \x01(\x0b\x32*.coresdk.workflow_activation.StartWorkflowH\x00\x12<\n\nfire_timer\x18\x02 \x01(\x0b\x32&.coresdk.workflow_activation.FireTimerH\x00\x12K\n\x12update_random_seed\x18\x04 \x01(\x0b\x32-.coresdk.workflow_activation.UpdateRandomSeedH\x00\x12\x44\n\x0equery_workflow\x18\x05 \x01(\x0b\x32*.coresdk.workflow_activation.QueryWorkflowH\x00\x12\x46\n\x0f\x63\x61ncel_workflow\x18\x06 \x01(\x0b\x32+.coresdk.workflow_activation.CancelWorkflowH\x00\x12\x46\n\x0fsignal_workflow\x18\x07 \x01(\x0b\x32+.coresdk.workflow_activation.SignalWorkflowH\x00\x12H\n\x10resolve_activity\x18\x08 \x01(\x0b\x32,.coresdk.workflow_activation.ResolveActivityH\x00\x12G\n\x10notify_has_patch\x18\t \x01(\x0b\x32+.coresdk.workflow_activation.NotifyHasPatchH\x00\x12q\n&resolve_child_workflow_execution_start\x18\n \x01(\x0b\x32?.coresdk.workflow_activation.ResolveChildWorkflowExecutionStartH\x00\x12\x66\n resolve_child_workflow_execution\x18\x0b \x01(\x0b\x32:.coresdk.workflow_activation.ResolveChildWorkflowExecutionH\x00\x12\x66\n resolve_signal_external_workflow\x18\x0c \x01(\x0b\x32:.coresdk.workflow_activation.ResolveSignalExternalWorkflowH\x00\x12u\n(resolve_request_cancel_external_workflow\x18\r \x01(\x0b\x32\x41.coresdk.workflow_activation.ResolveRequestCancelExternalWorkflowH\x00\x12:\n\tdo_update\x18\x0e \x01(\x0b\x32%.coresdk.workflow_activation.DoUpdateH\x00\x12I\n\x11remove_from_cache\x18\x32 \x01(\x0b\x32,.coresdk.workflow_activation.RemoveFromCacheH\x00\x42\t\n\x07variant"\xd9\t\n\rStartWorkflow\x12\x15\n\rworkflow_type\x18\x01 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x02 \x01(\t\x12\x32\n\targuments\x18\x03 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12\x17\n\x0frandomness_seed\x18\x04 \x01(\x04\x12H\n\x07headers\x18\x05 \x03(\x0b\x32\x37.coresdk.workflow_activation.StartWorkflow.HeadersEntry\x12\x10\n\x08identity\x18\x06 \x01(\t\x12I\n\x14parent_workflow_info\x18\x07 \x01(\x0b\x32+.coresdk.common.NamespacedWorkflowExecution\x12=\n\x1aworkflow_execution_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x37\n\x14workflow_run_timeout\x18\t \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x38\n\x15workflow_task_timeout\x18\n \x01(\x0b\x32\x19.google.protobuf.Duration\x12\'\n\x1f\x63ontinued_from_execution_run_id\x18\x0b \x01(\t\x12J\n\x13\x63ontinued_initiator\x18\x0c \x01(\x0e\x32-.temporal.api.enums.v1.ContinueAsNewInitiator\x12;\n\x11\x63ontinued_failure\x18\r \x01(\x0b\x32 .temporal.api.failure.v1.Failure\x12@\n\x16last_completion_result\x18\x0e \x01(\x0b\x32 .temporal.api.common.v1.Payloads\x12\x1e\n\x16\x66irst_execution_run_id\x18\x0f \x01(\t\x12\x39\n\x0cretry_policy\x18\x10 \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12\x0f\n\x07\x61ttempt\x18\x11 \x01(\x05\x12\x15\n\rcron_schedule\x18\x12 \x01(\t\x12\x46\n"workflow_execution_expiration_time\x18\x13 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x45\n"cron_schedule_to_schedule_interval\x18\x14 \x01(\x0b\x32\x19.google.protobuf.Duration\x12*\n\x04memo\x18\x15 \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo\x12\x43\n\x11search_attributes\x18\x16 \x01(\x0b\x32(.temporal.api.common.v1.SearchAttributes\x12.\n\nstart_time\x18\x17 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x1aO\n\x0cHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01"\x18\n\tFireTimer\x12\x0b\n\x03seq\x18\x01 \x01(\r"[\n\x0fResolveActivity\x12\x0b\n\x03seq\x18\x01 \x01(\r\x12;\n\x06result\x18\x02 \x01(\x0b\x32+.coresdk.activity_result.ActivityResolution"\xd1\x02\n"ResolveChildWorkflowExecutionStart\x12\x0b\n\x03seq\x18\x01 \x01(\r\x12[\n\tsucceeded\x18\x02 \x01(\x0b\x32\x46.coresdk.workflow_activation.ResolveChildWorkflowExecutionStartSuccessH\x00\x12X\n\x06\x66\x61iled\x18\x03 \x01(\x0b\x32\x46.coresdk.workflow_activation.ResolveChildWorkflowExecutionStartFailureH\x00\x12]\n\tcancelled\x18\x04 \x01(\x0b\x32H.coresdk.workflow_activation.ResolveChildWorkflowExecutionStartCancelledH\x00\x42\x08\n\x06status";\n)ResolveChildWorkflowExecutionStartSuccess\x12\x0e\n\x06run_id\x18\x01 \x01(\t"\xa6\x01\n)ResolveChildWorkflowExecutionStartFailure\x12\x13\n\x0bworkflow_id\x18\x01 \x01(\t\x12\x15\n\rworkflow_type\x18\x02 \x01(\t\x12M\n\x05\x63\x61use\x18\x03 \x01(\x0e\x32>.coresdk.child_workflow.StartChildWorkflowExecutionFailedCause"`\n+ResolveChildWorkflowExecutionStartCancelled\x12\x31\n\x07\x66\x61ilure\x18\x01 \x01(\x0b\x32 .temporal.api.failure.v1.Failure"i\n\x1dResolveChildWorkflowExecution\x12\x0b\n\x03seq\x18\x01 \x01(\r\x12;\n\x06result\x18\x02 \x01(\x0b\x32+.coresdk.child_workflow.ChildWorkflowResult"+\n\x10UpdateRandomSeed\x12\x17\n\x0frandomness_seed\x18\x01 \x01(\x04"\x84\x02\n\rQueryWorkflow\x12\x10\n\x08query_id\x18\x01 \x01(\t\x12\x12\n\nquery_type\x18\x02 \x01(\t\x12\x32\n\targuments\x18\x03 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12H\n\x07headers\x18\x05 \x03(\x0b\x32\x37.coresdk.workflow_activation.QueryWorkflow.HeadersEntry\x1aO\n\x0cHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01"B\n\x0e\x43\x61ncelWorkflow\x12\x30\n\x07\x64\x65tails\x18\x01 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload"\x83\x02\n\x0eSignalWorkflow\x12\x13\n\x0bsignal_name\x18\x01 \x01(\t\x12.\n\x05input\x18\x02 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12\x10\n\x08identity\x18\x03 \x01(\t\x12I\n\x07headers\x18\x05 \x03(\x0b\x32\x38.coresdk.workflow_activation.SignalWorkflow.HeadersEntry\x1aO\n\x0cHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01""\n\x0eNotifyHasPatch\x12\x10\n\x08patch_id\x18\x01 \x01(\t"_\n\x1dResolveSignalExternalWorkflow\x12\x0b\n\x03seq\x18\x01 \x01(\r\x12\x31\n\x07\x66\x61ilure\x18\x02 \x01(\x0b\x32 .temporal.api.failure.v1.Failure"f\n$ResolveRequestCancelExternalWorkflow\x12\x0b\n\x03seq\x18\x01 \x01(\r\x12\x31\n\x07\x66\x61ilure\x18\x02 \x01(\x0b\x32 .temporal.api.failure.v1.Failure"\xcb\x02\n\x08\x44oUpdate\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1c\n\x14protocol_instance_id\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12.\n\x05input\x18\x04 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12\x43\n\x07headers\x18\x05 \x03(\x0b\x32\x32.coresdk.workflow_activation.DoUpdate.HeadersEntry\x12*\n\x04meta\x18\x06 \x01(\x0b\x32\x1c.temporal.api.update.v1.Meta\x12\x15\n\rrun_validator\x18\x07 \x01(\x08\x1aO\n\x0cHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01"\xc1\x02\n\x0fRemoveFromCache\x12\x0f\n\x07message\x18\x01 \x01(\t\x12K\n\x06reason\x18\x02 \x01(\x0e\x32;.coresdk.workflow_activation.RemoveFromCache.EvictionReason"\xcf\x01\n\x0e\x45victionReason\x12\x0f\n\x0bUNSPECIFIED\x10\x00\x12\x0e\n\nCACHE_FULL\x10\x01\x12\x0e\n\nCACHE_MISS\x10\x02\x12\x12\n\x0eNONDETERMINISM\x10\x03\x12\r\n\tLANG_FAIL\x10\x04\x12\x12\n\x0eLANG_REQUESTED\x10\x05\x12\x12\n\x0eTASK_NOT_FOUND\x10\x06\x12\x15\n\x11UNHANDLED_COMMAND\x10\x07\x12\t\n\x05\x46\x41TAL\x10\x08\x12\x1f\n\x1bPAGINATION_OR_HISTORY_FETCH\x10\tB.\xea\x02+Temporalio::Bridge::Api::WorkflowActivationb\x06proto3' ) @@ -74,6 +77,8 @@ _RESOLVEREQUESTCANCELEXTERNALWORKFLOW = DESCRIPTOR.message_types_by_name[ "ResolveRequestCancelExternalWorkflow" ] +_DOUPDATE = DESCRIPTOR.message_types_by_name["DoUpdate"] +_DOUPDATE_HEADERSENTRY = _DOUPDATE.nested_types_by_name["HeadersEntry"] _REMOVEFROMCACHE = DESCRIPTOR.message_types_by_name["RemoveFromCache"] _REMOVEFROMCACHE_EVICTIONREASON = _REMOVEFROMCACHE.enum_types_by_name["EvictionReason"] WorkflowActivation = _reflection.GeneratedProtocolMessageType( @@ -293,6 +298,27 @@ ) _sym_db.RegisterMessage(ResolveRequestCancelExternalWorkflow) +DoUpdate = _reflection.GeneratedProtocolMessageType( + "DoUpdate", + (_message.Message,), + { + "HeadersEntry": _reflection.GeneratedProtocolMessageType( + "HeadersEntry", + (_message.Message,), + { + "DESCRIPTOR": _DOUPDATE_HEADERSENTRY, + "__module__": "temporal.sdk.core.workflow_activation.workflow_activation_pb2" + # @@protoc_insertion_point(class_scope:coresdk.workflow_activation.DoUpdate.HeadersEntry) + }, + ), + "DESCRIPTOR": _DOUPDATE, + "__module__": "temporal.sdk.core.workflow_activation.workflow_activation_pb2" + # @@protoc_insertion_point(class_scope:coresdk.workflow_activation.DoUpdate) + }, +) +_sym_db.RegisterMessage(DoUpdate) +_sym_db.RegisterMessage(DoUpdate.HeadersEntry) + RemoveFromCache = _reflection.GeneratedProtocolMessageType( "RemoveFromCache", (_message.Message,), @@ -315,48 +341,54 @@ _QUERYWORKFLOW_HEADERSENTRY._serialized_options = b"8\001" _SIGNALWORKFLOW_HEADERSENTRY._options = None _SIGNALWORKFLOW_HEADERSENTRY._serialized_options = b"8\001" - _WORKFLOWACTIVATION._serialized_start = 428 - _WORKFLOWACTIVATION._serialized_end = 720 - _WORKFLOWACTIVATIONJOB._serialized_start = 723 - _WORKFLOWACTIVATIONJOB._serialized_end = 1844 - _STARTWORKFLOW._serialized_start = 1847 - _STARTWORKFLOW._serialized_end = 3088 - _STARTWORKFLOW_HEADERSENTRY._serialized_start = 3009 - _STARTWORKFLOW_HEADERSENTRY._serialized_end = 3088 - _FIRETIMER._serialized_start = 3090 - _FIRETIMER._serialized_end = 3114 - _RESOLVEACTIVITY._serialized_start = 3116 - _RESOLVEACTIVITY._serialized_end = 3207 - _RESOLVECHILDWORKFLOWEXECUTIONSTART._serialized_start = 3210 - _RESOLVECHILDWORKFLOWEXECUTIONSTART._serialized_end = 3547 - _RESOLVECHILDWORKFLOWEXECUTIONSTARTSUCCESS._serialized_start = 3549 - _RESOLVECHILDWORKFLOWEXECUTIONSTARTSUCCESS._serialized_end = 3608 - _RESOLVECHILDWORKFLOWEXECUTIONSTARTFAILURE._serialized_start = 3611 - _RESOLVECHILDWORKFLOWEXECUTIONSTARTFAILURE._serialized_end = 3777 - _RESOLVECHILDWORKFLOWEXECUTIONSTARTCANCELLED._serialized_start = 3779 - _RESOLVECHILDWORKFLOWEXECUTIONSTARTCANCELLED._serialized_end = 3875 - _RESOLVECHILDWORKFLOWEXECUTION._serialized_start = 3877 - _RESOLVECHILDWORKFLOWEXECUTION._serialized_end = 3982 - _UPDATERANDOMSEED._serialized_start = 3984 - _UPDATERANDOMSEED._serialized_end = 4027 - _QUERYWORKFLOW._serialized_start = 4030 - _QUERYWORKFLOW._serialized_end = 4290 - _QUERYWORKFLOW_HEADERSENTRY._serialized_start = 3009 - _QUERYWORKFLOW_HEADERSENTRY._serialized_end = 3088 - _CANCELWORKFLOW._serialized_start = 4292 - _CANCELWORKFLOW._serialized_end = 4358 - _SIGNALWORKFLOW._serialized_start = 4361 - _SIGNALWORKFLOW._serialized_end = 4620 - _SIGNALWORKFLOW_HEADERSENTRY._serialized_start = 3009 - _SIGNALWORKFLOW_HEADERSENTRY._serialized_end = 3088 - _NOTIFYHASPATCH._serialized_start = 4622 - _NOTIFYHASPATCH._serialized_end = 4656 - _RESOLVESIGNALEXTERNALWORKFLOW._serialized_start = 4658 - _RESOLVESIGNALEXTERNALWORKFLOW._serialized_end = 4753 - _RESOLVEREQUESTCANCELEXTERNALWORKFLOW._serialized_start = 4755 - _RESOLVEREQUESTCANCELEXTERNALWORKFLOW._serialized_end = 4857 - _REMOVEFROMCACHE._serialized_start = 4860 - _REMOVEFROMCACHE._serialized_end = 5181 - _REMOVEFROMCACHE_EVICTIONREASON._serialized_start = 4974 - _REMOVEFROMCACHE_EVICTIONREASON._serialized_end = 5181 + _DOUPDATE_HEADERSENTRY._options = None + _DOUPDATE_HEADERSENTRY._serialized_options = b"8\001" + _WORKFLOWACTIVATION._serialized_start = 466 + _WORKFLOWACTIVATION._serialized_end = 758 + _WORKFLOWACTIVATIONJOB._serialized_start = 761 + _WORKFLOWACTIVATIONJOB._serialized_end = 1942 + _STARTWORKFLOW._serialized_start = 1945 + _STARTWORKFLOW._serialized_end = 3186 + _STARTWORKFLOW_HEADERSENTRY._serialized_start = 3107 + _STARTWORKFLOW_HEADERSENTRY._serialized_end = 3186 + _FIRETIMER._serialized_start = 3188 + _FIRETIMER._serialized_end = 3212 + _RESOLVEACTIVITY._serialized_start = 3214 + _RESOLVEACTIVITY._serialized_end = 3305 + _RESOLVECHILDWORKFLOWEXECUTIONSTART._serialized_start = 3308 + _RESOLVECHILDWORKFLOWEXECUTIONSTART._serialized_end = 3645 + _RESOLVECHILDWORKFLOWEXECUTIONSTARTSUCCESS._serialized_start = 3647 + _RESOLVECHILDWORKFLOWEXECUTIONSTARTSUCCESS._serialized_end = 3706 + _RESOLVECHILDWORKFLOWEXECUTIONSTARTFAILURE._serialized_start = 3709 + _RESOLVECHILDWORKFLOWEXECUTIONSTARTFAILURE._serialized_end = 3875 + _RESOLVECHILDWORKFLOWEXECUTIONSTARTCANCELLED._serialized_start = 3877 + _RESOLVECHILDWORKFLOWEXECUTIONSTARTCANCELLED._serialized_end = 3973 + _RESOLVECHILDWORKFLOWEXECUTION._serialized_start = 3975 + _RESOLVECHILDWORKFLOWEXECUTION._serialized_end = 4080 + _UPDATERANDOMSEED._serialized_start = 4082 + _UPDATERANDOMSEED._serialized_end = 4125 + _QUERYWORKFLOW._serialized_start = 4128 + _QUERYWORKFLOW._serialized_end = 4388 + _QUERYWORKFLOW_HEADERSENTRY._serialized_start = 3107 + _QUERYWORKFLOW_HEADERSENTRY._serialized_end = 3186 + _CANCELWORKFLOW._serialized_start = 4390 + _CANCELWORKFLOW._serialized_end = 4456 + _SIGNALWORKFLOW._serialized_start = 4459 + _SIGNALWORKFLOW._serialized_end = 4718 + _SIGNALWORKFLOW_HEADERSENTRY._serialized_start = 3107 + _SIGNALWORKFLOW_HEADERSENTRY._serialized_end = 3186 + _NOTIFYHASPATCH._serialized_start = 4720 + _NOTIFYHASPATCH._serialized_end = 4754 + _RESOLVESIGNALEXTERNALWORKFLOW._serialized_start = 4756 + _RESOLVESIGNALEXTERNALWORKFLOW._serialized_end = 4851 + _RESOLVEREQUESTCANCELEXTERNALWORKFLOW._serialized_start = 4853 + _RESOLVEREQUESTCANCELEXTERNALWORKFLOW._serialized_end = 4955 + _DOUPDATE._serialized_start = 4958 + _DOUPDATE._serialized_end = 5289 + _DOUPDATE_HEADERSENTRY._serialized_start = 3107 + _DOUPDATE_HEADERSENTRY._serialized_end = 3186 + _REMOVEFROMCACHE._serialized_start = 5292 + _REMOVEFROMCACHE._serialized_end = 5613 + _REMOVEFROMCACHE_EVICTIONREASON._serialized_start = 5406 + _REMOVEFROMCACHE_EVICTIONREASON._serialized_end = 5613 # @@protoc_insertion_point(module_scope) diff --git a/temporalio/bridge/proto/workflow_activation/workflow_activation_pb2.pyi b/temporalio/bridge/proto/workflow_activation/workflow_activation_pb2.pyi index 557fe9f3..1be4a31a 100644 --- a/temporalio/bridge/proto/workflow_activation/workflow_activation_pb2.pyi +++ b/temporalio/bridge/proto/workflow_activation/workflow_activation_pb2.pyi @@ -17,6 +17,7 @@ import sys import temporalio.api.common.v1.message_pb2 import temporalio.api.enums.v1.workflow_pb2 import temporalio.api.failure.v1.message_pb2 +import temporalio.api.update.v1.message_pb2 import temporalio.bridge.proto.activity_result.activity_result_pb2 import temporalio.bridge.proto.child_workflow.child_workflow_pb2 import temporalio.bridge.proto.common.common_pb2 @@ -130,6 +131,7 @@ class WorkflowActivationJob(google.protobuf.message.Message): RESOLVE_CHILD_WORKFLOW_EXECUTION_FIELD_NUMBER: builtins.int RESOLVE_SIGNAL_EXTERNAL_WORKFLOW_FIELD_NUMBER: builtins.int RESOLVE_REQUEST_CANCEL_EXTERNAL_WORKFLOW_FIELD_NUMBER: builtins.int + DO_UPDATE_FIELD_NUMBER: builtins.int REMOVE_FROM_CACHE_FIELD_NUMBER: builtins.int @property def start_workflow(self) -> global___StartWorkflow: @@ -179,6 +181,9 @@ class WorkflowActivationJob(google.protobuf.message.Message): ) -> global___ResolveRequestCancelExternalWorkflow: """An attempt to cancel an external workflow resolved""" @property + def do_update(self) -> global___DoUpdate: + """A request to handle a workflow update.""" + @property def remove_from_cache(self) -> global___RemoveFromCache: """Remove the workflow identified by the [WorkflowActivation] containing this job from the cache after performing the activation. @@ -205,6 +210,7 @@ class WorkflowActivationJob(google.protobuf.message.Message): | None = ..., resolve_request_cancel_external_workflow: global___ResolveRequestCancelExternalWorkflow | None = ..., + do_update: global___DoUpdate | None = ..., remove_from_cache: global___RemoveFromCache | None = ..., ) -> None: ... def HasField( @@ -212,6 +218,8 @@ class WorkflowActivationJob(google.protobuf.message.Message): field_name: typing_extensions.Literal[ "cancel_workflow", b"cancel_workflow", + "do_update", + b"do_update", "fire_timer", b"fire_timer", "notify_has_patch", @@ -245,6 +253,8 @@ class WorkflowActivationJob(google.protobuf.message.Message): field_name: typing_extensions.Literal[ "cancel_workflow", b"cancel_workflow", + "do_update", + b"do_update", "fire_timer", b"fire_timer", "notify_has_patch", @@ -289,6 +299,7 @@ class WorkflowActivationJob(google.protobuf.message.Message): "resolve_child_workflow_execution", "resolve_signal_external_workflow", "resolve_request_cancel_external_workflow", + "do_update", "remove_from_cache", ] | None @@ -1056,6 +1067,114 @@ class ResolveRequestCancelExternalWorkflow(google.protobuf.message.Message): global___ResolveRequestCancelExternalWorkflow = ResolveRequestCancelExternalWorkflow +class DoUpdate(google.protobuf.message.Message): + """Lang is requested to invoke an update handler on the workflow. Lang should invoke the update + validator first (if requested). If it accepts the update, immediately invoke the update handler. + Lang must reply to the activation containing this job with an `UpdateResponse`. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class HeadersEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + @property + def value(self) -> temporalio.api.common.v1.message_pb2.Payload: ... + def __init__( + self, + *, + key: builtins.str = ..., + value: temporalio.api.common.v1.message_pb2.Payload | None = ..., + ) -> None: ... + def HasField( + self, field_name: typing_extensions.Literal["value", b"value"] + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal["key", b"key", "value", b"value"], + ) -> None: ... + + ID_FIELD_NUMBER: builtins.int + PROTOCOL_INSTANCE_ID_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + INPUT_FIELD_NUMBER: builtins.int + HEADERS_FIELD_NUMBER: builtins.int + META_FIELD_NUMBER: builtins.int + RUN_VALIDATOR_FIELD_NUMBER: builtins.int + id: builtins.str + """A workflow-unique identifier for this update""" + protocol_instance_id: builtins.str + """The protocol message instance ID - this is used to uniquely track the ID server side and + internally. + """ + name: builtins.str + """The name of the update handler""" + @property + def input( + self, + ) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[ + temporalio.api.common.v1.message_pb2.Payload + ]: + """The input to the update""" + @property + def headers( + self, + ) -> google.protobuf.internal.containers.MessageMap[ + builtins.str, temporalio.api.common.v1.message_pb2.Payload + ]: + """Headers attached to the update""" + @property + def meta(self) -> temporalio.api.update.v1.message_pb2.Meta: + """Remaining metadata associated with the update. The `update_id` field is stripped from here + and moved to `id`, since it is guaranteed to be present. + """ + run_validator: builtins.bool + """If set true, lang must run the update's validator before running the handler. This will be + set false during replay, since validation is not re-run during replay. + """ + def __init__( + self, + *, + id: builtins.str = ..., + protocol_instance_id: builtins.str = ..., + name: builtins.str = ..., + input: collections.abc.Iterable[temporalio.api.common.v1.message_pb2.Payload] + | None = ..., + headers: collections.abc.Mapping[ + builtins.str, temporalio.api.common.v1.message_pb2.Payload + ] + | None = ..., + meta: temporalio.api.update.v1.message_pb2.Meta | None = ..., + run_validator: builtins.bool = ..., + ) -> None: ... + def HasField( + self, field_name: typing_extensions.Literal["meta", b"meta"] + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "headers", + b"headers", + "id", + b"id", + "input", + b"input", + "meta", + b"meta", + "name", + b"name", + "protocol_instance_id", + b"protocol_instance_id", + "run_validator", + b"run_validator", + ], + ) -> None: ... + +global___DoUpdate = DoUpdate + class RemoveFromCache(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor diff --git a/temporalio/bridge/proto/workflow_commands/__init__.py b/temporalio/bridge/proto/workflow_commands/__init__.py index 2007a970..00008983 100644 --- a/temporalio/bridge/proto/workflow_commands/__init__.py +++ b/temporalio/bridge/proto/workflow_commands/__init__.py @@ -19,6 +19,7 @@ SignalExternalWorkflowExecution, StartChildWorkflowExecution, StartTimer, + UpdateResponse, UpsertWorkflowSearchAttributes, WorkflowCommand, ) @@ -44,6 +45,7 @@ "SignalExternalWorkflowExecution", "StartChildWorkflowExecution", "StartTimer", + "UpdateResponse", "UpsertWorkflowSearchAttributes", "WorkflowCommand", ] diff --git a/temporalio/bridge/proto/workflow_commands/workflow_commands_pb2.py b/temporalio/bridge/proto/workflow_commands/workflow_commands_pb2.py index b2ecd469..a22b58c9 100644 --- a/temporalio/bridge/proto/workflow_commands/workflow_commands_pb2.py +++ b/temporalio/bridge/proto/workflow_commands/workflow_commands_pb2.py @@ -15,6 +15,7 @@ from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 +from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2 from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 from temporalio.api.common.v1 import ( @@ -34,7 +35,7 @@ ) DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n;temporal/sdk/core/workflow_commands/workflow_commands.proto\x12\x19\x63oresdk.workflow_commands\x1a\x1egoogle/protobuf/duration.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a$temporal/api/common/v1/message.proto\x1a$temporal/api/enums/v1/workflow.proto\x1a%temporal/api/failure/v1/message.proto\x1a\x35temporal/sdk/core/child_workflow/child_workflow.proto\x1a%temporal/sdk/core/common/common.proto"\xac\r\n\x0fWorkflowCommand\x12<\n\x0bstart_timer\x18\x01 \x01(\x0b\x32%.coresdk.workflow_commands.StartTimerH\x00\x12H\n\x11schedule_activity\x18\x02 \x01(\x0b\x32+.coresdk.workflow_commands.ScheduleActivityH\x00\x12\x42\n\x10respond_to_query\x18\x03 \x01(\x0b\x32&.coresdk.workflow_commands.QueryResultH\x00\x12S\n\x17request_cancel_activity\x18\x04 \x01(\x0b\x32\x30.coresdk.workflow_commands.RequestCancelActivityH\x00\x12>\n\x0c\x63\x61ncel_timer\x18\x05 \x01(\x0b\x32&.coresdk.workflow_commands.CancelTimerH\x00\x12[\n\x1b\x63omplete_workflow_execution\x18\x06 \x01(\x0b\x32\x34.coresdk.workflow_commands.CompleteWorkflowExecutionH\x00\x12S\n\x17\x66\x61il_workflow_execution\x18\x07 \x01(\x0b\x32\x30.coresdk.workflow_commands.FailWorkflowExecutionH\x00\x12g\n"continue_as_new_workflow_execution\x18\x08 \x01(\x0b\x32\x39.coresdk.workflow_commands.ContinueAsNewWorkflowExecutionH\x00\x12W\n\x19\x63\x61ncel_workflow_execution\x18\t \x01(\x0b\x32\x32.coresdk.workflow_commands.CancelWorkflowExecutionH\x00\x12\x45\n\x10set_patch_marker\x18\n \x01(\x0b\x32).coresdk.workflow_commands.SetPatchMarkerH\x00\x12`\n\x1estart_child_workflow_execution\x18\x0b \x01(\x0b\x32\x36.coresdk.workflow_commands.StartChildWorkflowExecutionH\x00\x12\x62\n\x1f\x63\x61ncel_child_workflow_execution\x18\x0c \x01(\x0b\x32\x37.coresdk.workflow_commands.CancelChildWorkflowExecutionH\x00\x12w\n*request_cancel_external_workflow_execution\x18\r \x01(\x0b\x32\x41.coresdk.workflow_commands.RequestCancelExternalWorkflowExecutionH\x00\x12h\n"signal_external_workflow_execution\x18\x0e \x01(\x0b\x32:.coresdk.workflow_commands.SignalExternalWorkflowExecutionH\x00\x12Q\n\x16\x63\x61ncel_signal_workflow\x18\x0f \x01(\x0b\x32/.coresdk.workflow_commands.CancelSignalWorkflowH\x00\x12S\n\x17schedule_local_activity\x18\x10 \x01(\x0b\x32\x30.coresdk.workflow_commands.ScheduleLocalActivityH\x00\x12^\n\x1drequest_cancel_local_activity\x18\x11 \x01(\x0b\x32\x35.coresdk.workflow_commands.RequestCancelLocalActivityH\x00\x12\x66\n!upsert_workflow_search_attributes\x18\x12 \x01(\x0b\x32\x39.coresdk.workflow_commands.UpsertWorkflowSearchAttributesH\x00\x12Y\n\x1amodify_workflow_properties\x18\x13 \x01(\x0b\x32\x33.coresdk.workflow_commands.ModifyWorkflowPropertiesH\x00\x42\t\n\x07variant"S\n\nStartTimer\x12\x0b\n\x03seq\x18\x01 \x01(\r\x12\x38\n\x15start_to_fire_timeout\x18\x02 \x01(\x0b\x32\x19.google.protobuf.Duration"\x1a\n\x0b\x43\x61ncelTimer\x12\x0b\n\x03seq\x18\x01 \x01(\r"\x84\x06\n\x10ScheduleActivity\x12\x0b\n\x03seq\x18\x01 \x01(\r\x12\x13\n\x0b\x61\x63tivity_id\x18\x02 \x01(\t\x12\x15\n\ractivity_type\x18\x03 \x01(\t\x12\x12\n\ntask_queue\x18\x05 \x01(\t\x12I\n\x07headers\x18\x06 \x03(\x0b\x32\x38.coresdk.workflow_commands.ScheduleActivity.HeadersEntry\x12\x32\n\targuments\x18\x07 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12<\n\x19schedule_to_close_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12<\n\x19schedule_to_start_timeout\x18\t \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x16start_to_close_timeout\x18\n \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x34\n\x11heartbeat_timeout\x18\x0b \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x0cretry_policy\x18\x0c \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12N\n\x11\x63\x61ncellation_type\x18\r \x01(\x0e\x32\x33.coresdk.workflow_commands.ActivityCancellationType\x12\x1e\n\x16\x64o_not_eagerly_execute\x18\x0e \x01(\x08\x12;\n\x11versioning_intent\x18\x0f \x01(\x0e\x32 .coresdk.common.VersioningIntent\x1aO\n\x0cHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01"\xee\x05\n\x15ScheduleLocalActivity\x12\x0b\n\x03seq\x18\x01 \x01(\r\x12\x13\n\x0b\x61\x63tivity_id\x18\x02 \x01(\t\x12\x15\n\ractivity_type\x18\x03 \x01(\t\x12\x0f\n\x07\x61ttempt\x18\x04 \x01(\r\x12:\n\x16original_schedule_time\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12N\n\x07headers\x18\x06 \x03(\x0b\x32=.coresdk.workflow_commands.ScheduleLocalActivity.HeadersEntry\x12\x32\n\targuments\x18\x07 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12<\n\x19schedule_to_close_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12<\n\x19schedule_to_start_timeout\x18\t \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x16start_to_close_timeout\x18\n \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x0cretry_policy\x18\x0b \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12\x38\n\x15local_retry_threshold\x18\x0c \x01(\x0b\x32\x19.google.protobuf.Duration\x12N\n\x11\x63\x61ncellation_type\x18\r \x01(\x0e\x32\x33.coresdk.workflow_commands.ActivityCancellationType\x1aO\n\x0cHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01"$\n\x15RequestCancelActivity\x12\x0b\n\x03seq\x18\x01 \x01(\r")\n\x1aRequestCancelLocalActivity\x12\x0b\n\x03seq\x18\x01 \x01(\r"\x9c\x01\n\x0bQueryResult\x12\x10\n\x08query_id\x18\x01 \x01(\t\x12<\n\tsucceeded\x18\x02 \x01(\x0b\x32\'.coresdk.workflow_commands.QuerySuccessH\x00\x12\x32\n\x06\x66\x61iled\x18\x03 \x01(\x0b\x32 .temporal.api.failure.v1.FailureH\x00\x42\t\n\x07variant"A\n\x0cQuerySuccess\x12\x31\n\x08response\x18\x01 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload"L\n\x19\x43ompleteWorkflowExecution\x12/\n\x06result\x18\x01 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload"J\n\x15\x46\x61ilWorkflowExecution\x12\x31\n\x07\x66\x61ilure\x18\x01 \x01(\x0b\x32 .temporal.api.failure.v1.Failure"\xfb\x06\n\x1e\x43ontinueAsNewWorkflowExecution\x12\x15\n\rworkflow_type\x18\x01 \x01(\t\x12\x12\n\ntask_queue\x18\x02 \x01(\t\x12\x32\n\targuments\x18\x03 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12\x37\n\x14workflow_run_timeout\x18\x04 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x38\n\x15workflow_task_timeout\x18\x05 \x01(\x0b\x32\x19.google.protobuf.Duration\x12Q\n\x04memo\x18\x06 \x03(\x0b\x32\x43.coresdk.workflow_commands.ContinueAsNewWorkflowExecution.MemoEntry\x12W\n\x07headers\x18\x07 \x03(\x0b\x32\x46.coresdk.workflow_commands.ContinueAsNewWorkflowExecution.HeadersEntry\x12j\n\x11search_attributes\x18\x08 \x03(\x0b\x32O.coresdk.workflow_commands.ContinueAsNewWorkflowExecution.SearchAttributesEntry\x12\x39\n\x0cretry_policy\x18\t \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12;\n\x11versioning_intent\x18\n \x01(\x0e\x32 .coresdk.common.VersioningIntent\x1aL\n\tMemoEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01\x1aO\n\x0cHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01\x1aX\n\x15SearchAttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01"\x19\n\x17\x43\x61ncelWorkflowExecution"6\n\x0eSetPatchMarker\x12\x10\n\x08patch_id\x18\x01 \x01(\t\x12\x12\n\ndeprecated\x18\x02 \x01(\x08"\xe0\t\n\x1bStartChildWorkflowExecution\x12\x0b\n\x03seq\x18\x01 \x01(\r\x12\x11\n\tnamespace\x18\x02 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x03 \x01(\t\x12\x15\n\rworkflow_type\x18\x04 \x01(\t\x12\x12\n\ntask_queue\x18\x05 \x01(\t\x12.\n\x05input\x18\x06 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12=\n\x1aworkflow_execution_timeout\x18\x07 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x37\n\x14workflow_run_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x38\n\x15workflow_task_timeout\x18\t \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x46\n\x13parent_close_policy\x18\n \x01(\x0e\x32).coresdk.child_workflow.ParentClosePolicy\x12N\n\x18workflow_id_reuse_policy\x18\x0c \x01(\x0e\x32,.temporal.api.enums.v1.WorkflowIdReusePolicy\x12\x39\n\x0cretry_policy\x18\r \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12\x15\n\rcron_schedule\x18\x0e \x01(\t\x12T\n\x07headers\x18\x0f \x03(\x0b\x32\x43.coresdk.workflow_commands.StartChildWorkflowExecution.HeadersEntry\x12N\n\x04memo\x18\x10 \x03(\x0b\x32@.coresdk.workflow_commands.StartChildWorkflowExecution.MemoEntry\x12g\n\x11search_attributes\x18\x11 \x03(\x0b\x32L.coresdk.workflow_commands.StartChildWorkflowExecution.SearchAttributesEntry\x12P\n\x11\x63\x61ncellation_type\x18\x12 \x01(\x0e\x32\x35.coresdk.child_workflow.ChildWorkflowCancellationType\x12;\n\x11versioning_intent\x18\x13 \x01(\x0e\x32 .coresdk.common.VersioningIntent\x1aO\n\x0cHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01\x1aL\n\tMemoEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01\x1aX\n\x15SearchAttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01":\n\x1c\x43\x61ncelChildWorkflowExecution\x12\x1a\n\x12\x63hild_workflow_seq\x18\x01 \x01(\r"\xa7\x01\n&RequestCancelExternalWorkflowExecution\x12\x0b\n\x03seq\x18\x01 \x01(\r\x12I\n\x12workflow_execution\x18\x02 \x01(\x0b\x32+.coresdk.common.NamespacedWorkflowExecutionH\x00\x12\x1b\n\x11\x63hild_workflow_id\x18\x03 \x01(\tH\x00\x42\x08\n\x06target"\x8f\x03\n\x1fSignalExternalWorkflowExecution\x12\x0b\n\x03seq\x18\x01 \x01(\r\x12I\n\x12workflow_execution\x18\x02 \x01(\x0b\x32+.coresdk.common.NamespacedWorkflowExecutionH\x00\x12\x1b\n\x11\x63hild_workflow_id\x18\x03 \x01(\tH\x00\x12\x13\n\x0bsignal_name\x18\x04 \x01(\t\x12-\n\x04\x61rgs\x18\x05 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12X\n\x07headers\x18\x06 \x03(\x0b\x32G.coresdk.workflow_commands.SignalExternalWorkflowExecution.HeadersEntry\x1aO\n\x0cHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01\x42\x08\n\x06target"#\n\x14\x43\x61ncelSignalWorkflow\x12\x0b\n\x03seq\x18\x01 \x01(\r"\xe6\x01\n\x1eUpsertWorkflowSearchAttributes\x12j\n\x11search_attributes\x18\x01 \x03(\x0b\x32O.coresdk.workflow_commands.UpsertWorkflowSearchAttributes.SearchAttributesEntry\x1aX\n\x15SearchAttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01"O\n\x18ModifyWorkflowProperties\x12\x33\n\rupserted_memo\x18\x01 \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo*X\n\x18\x41\x63tivityCancellationType\x12\x0e\n\nTRY_CANCEL\x10\x00\x12\x1f\n\x1bWAIT_CANCELLATION_COMPLETED\x10\x01\x12\x0b\n\x07\x41\x42\x41NDON\x10\x02\x42,\xea\x02)Temporalio::Bridge::Api::WorkflowCommandsb\x06proto3' + b'\n;temporal/sdk/core/workflow_commands/workflow_commands.proto\x12\x19\x63oresdk.workflow_commands\x1a\x1egoogle/protobuf/duration.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a$temporal/api/common/v1/message.proto\x1a$temporal/api/enums/v1/workflow.proto\x1a%temporal/api/failure/v1/message.proto\x1a\x35temporal/sdk/core/child_workflow/child_workflow.proto\x1a%temporal/sdk/core/common/common.proto"\xf2\r\n\x0fWorkflowCommand\x12<\n\x0bstart_timer\x18\x01 \x01(\x0b\x32%.coresdk.workflow_commands.StartTimerH\x00\x12H\n\x11schedule_activity\x18\x02 \x01(\x0b\x32+.coresdk.workflow_commands.ScheduleActivityH\x00\x12\x42\n\x10respond_to_query\x18\x03 \x01(\x0b\x32&.coresdk.workflow_commands.QueryResultH\x00\x12S\n\x17request_cancel_activity\x18\x04 \x01(\x0b\x32\x30.coresdk.workflow_commands.RequestCancelActivityH\x00\x12>\n\x0c\x63\x61ncel_timer\x18\x05 \x01(\x0b\x32&.coresdk.workflow_commands.CancelTimerH\x00\x12[\n\x1b\x63omplete_workflow_execution\x18\x06 \x01(\x0b\x32\x34.coresdk.workflow_commands.CompleteWorkflowExecutionH\x00\x12S\n\x17\x66\x61il_workflow_execution\x18\x07 \x01(\x0b\x32\x30.coresdk.workflow_commands.FailWorkflowExecutionH\x00\x12g\n"continue_as_new_workflow_execution\x18\x08 \x01(\x0b\x32\x39.coresdk.workflow_commands.ContinueAsNewWorkflowExecutionH\x00\x12W\n\x19\x63\x61ncel_workflow_execution\x18\t \x01(\x0b\x32\x32.coresdk.workflow_commands.CancelWorkflowExecutionH\x00\x12\x45\n\x10set_patch_marker\x18\n \x01(\x0b\x32).coresdk.workflow_commands.SetPatchMarkerH\x00\x12`\n\x1estart_child_workflow_execution\x18\x0b \x01(\x0b\x32\x36.coresdk.workflow_commands.StartChildWorkflowExecutionH\x00\x12\x62\n\x1f\x63\x61ncel_child_workflow_execution\x18\x0c \x01(\x0b\x32\x37.coresdk.workflow_commands.CancelChildWorkflowExecutionH\x00\x12w\n*request_cancel_external_workflow_execution\x18\r \x01(\x0b\x32\x41.coresdk.workflow_commands.RequestCancelExternalWorkflowExecutionH\x00\x12h\n"signal_external_workflow_execution\x18\x0e \x01(\x0b\x32:.coresdk.workflow_commands.SignalExternalWorkflowExecutionH\x00\x12Q\n\x16\x63\x61ncel_signal_workflow\x18\x0f \x01(\x0b\x32/.coresdk.workflow_commands.CancelSignalWorkflowH\x00\x12S\n\x17schedule_local_activity\x18\x10 \x01(\x0b\x32\x30.coresdk.workflow_commands.ScheduleLocalActivityH\x00\x12^\n\x1drequest_cancel_local_activity\x18\x11 \x01(\x0b\x32\x35.coresdk.workflow_commands.RequestCancelLocalActivityH\x00\x12\x66\n!upsert_workflow_search_attributes\x18\x12 \x01(\x0b\x32\x39.coresdk.workflow_commands.UpsertWorkflowSearchAttributesH\x00\x12Y\n\x1amodify_workflow_properties\x18\x13 \x01(\x0b\x32\x33.coresdk.workflow_commands.ModifyWorkflowPropertiesH\x00\x12\x44\n\x0fupdate_response\x18\x14 \x01(\x0b\x32).coresdk.workflow_commands.UpdateResponseH\x00\x42\t\n\x07variant"S\n\nStartTimer\x12\x0b\n\x03seq\x18\x01 \x01(\r\x12\x38\n\x15start_to_fire_timeout\x18\x02 \x01(\x0b\x32\x19.google.protobuf.Duration"\x1a\n\x0b\x43\x61ncelTimer\x12\x0b\n\x03seq\x18\x01 \x01(\r"\x84\x06\n\x10ScheduleActivity\x12\x0b\n\x03seq\x18\x01 \x01(\r\x12\x13\n\x0b\x61\x63tivity_id\x18\x02 \x01(\t\x12\x15\n\ractivity_type\x18\x03 \x01(\t\x12\x12\n\ntask_queue\x18\x05 \x01(\t\x12I\n\x07headers\x18\x06 \x03(\x0b\x32\x38.coresdk.workflow_commands.ScheduleActivity.HeadersEntry\x12\x32\n\targuments\x18\x07 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12<\n\x19schedule_to_close_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12<\n\x19schedule_to_start_timeout\x18\t \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x16start_to_close_timeout\x18\n \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x34\n\x11heartbeat_timeout\x18\x0b \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x0cretry_policy\x18\x0c \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12N\n\x11\x63\x61ncellation_type\x18\r \x01(\x0e\x32\x33.coresdk.workflow_commands.ActivityCancellationType\x12\x1e\n\x16\x64o_not_eagerly_execute\x18\x0e \x01(\x08\x12;\n\x11versioning_intent\x18\x0f \x01(\x0e\x32 .coresdk.common.VersioningIntent\x1aO\n\x0cHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01"\xee\x05\n\x15ScheduleLocalActivity\x12\x0b\n\x03seq\x18\x01 \x01(\r\x12\x13\n\x0b\x61\x63tivity_id\x18\x02 \x01(\t\x12\x15\n\ractivity_type\x18\x03 \x01(\t\x12\x0f\n\x07\x61ttempt\x18\x04 \x01(\r\x12:\n\x16original_schedule_time\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12N\n\x07headers\x18\x06 \x03(\x0b\x32=.coresdk.workflow_commands.ScheduleLocalActivity.HeadersEntry\x12\x32\n\targuments\x18\x07 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12<\n\x19schedule_to_close_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12<\n\x19schedule_to_start_timeout\x18\t \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x16start_to_close_timeout\x18\n \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x0cretry_policy\x18\x0b \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12\x38\n\x15local_retry_threshold\x18\x0c \x01(\x0b\x32\x19.google.protobuf.Duration\x12N\n\x11\x63\x61ncellation_type\x18\r \x01(\x0e\x32\x33.coresdk.workflow_commands.ActivityCancellationType\x1aO\n\x0cHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01"$\n\x15RequestCancelActivity\x12\x0b\n\x03seq\x18\x01 \x01(\r")\n\x1aRequestCancelLocalActivity\x12\x0b\n\x03seq\x18\x01 \x01(\r"\x9c\x01\n\x0bQueryResult\x12\x10\n\x08query_id\x18\x01 \x01(\t\x12<\n\tsucceeded\x18\x02 \x01(\x0b\x32\'.coresdk.workflow_commands.QuerySuccessH\x00\x12\x32\n\x06\x66\x61iled\x18\x03 \x01(\x0b\x32 .temporal.api.failure.v1.FailureH\x00\x42\t\n\x07variant"A\n\x0cQuerySuccess\x12\x31\n\x08response\x18\x01 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload"L\n\x19\x43ompleteWorkflowExecution\x12/\n\x06result\x18\x01 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload"J\n\x15\x46\x61ilWorkflowExecution\x12\x31\n\x07\x66\x61ilure\x18\x01 \x01(\x0b\x32 .temporal.api.failure.v1.Failure"\xfb\x06\n\x1e\x43ontinueAsNewWorkflowExecution\x12\x15\n\rworkflow_type\x18\x01 \x01(\t\x12\x12\n\ntask_queue\x18\x02 \x01(\t\x12\x32\n\targuments\x18\x03 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12\x37\n\x14workflow_run_timeout\x18\x04 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x38\n\x15workflow_task_timeout\x18\x05 \x01(\x0b\x32\x19.google.protobuf.Duration\x12Q\n\x04memo\x18\x06 \x03(\x0b\x32\x43.coresdk.workflow_commands.ContinueAsNewWorkflowExecution.MemoEntry\x12W\n\x07headers\x18\x07 \x03(\x0b\x32\x46.coresdk.workflow_commands.ContinueAsNewWorkflowExecution.HeadersEntry\x12j\n\x11search_attributes\x18\x08 \x03(\x0b\x32O.coresdk.workflow_commands.ContinueAsNewWorkflowExecution.SearchAttributesEntry\x12\x39\n\x0cretry_policy\x18\t \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12;\n\x11versioning_intent\x18\n \x01(\x0e\x32 .coresdk.common.VersioningIntent\x1aL\n\tMemoEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01\x1aO\n\x0cHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01\x1aX\n\x15SearchAttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01"\x19\n\x17\x43\x61ncelWorkflowExecution"6\n\x0eSetPatchMarker\x12\x10\n\x08patch_id\x18\x01 \x01(\t\x12\x12\n\ndeprecated\x18\x02 \x01(\x08"\xe0\t\n\x1bStartChildWorkflowExecution\x12\x0b\n\x03seq\x18\x01 \x01(\r\x12\x11\n\tnamespace\x18\x02 \x01(\t\x12\x13\n\x0bworkflow_id\x18\x03 \x01(\t\x12\x15\n\rworkflow_type\x18\x04 \x01(\t\x12\x12\n\ntask_queue\x18\x05 \x01(\t\x12.\n\x05input\x18\x06 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12=\n\x1aworkflow_execution_timeout\x18\x07 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x37\n\x14workflow_run_timeout\x18\x08 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x38\n\x15workflow_task_timeout\x18\t \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x46\n\x13parent_close_policy\x18\n \x01(\x0e\x32).coresdk.child_workflow.ParentClosePolicy\x12N\n\x18workflow_id_reuse_policy\x18\x0c \x01(\x0e\x32,.temporal.api.enums.v1.WorkflowIdReusePolicy\x12\x39\n\x0cretry_policy\x18\r \x01(\x0b\x32#.temporal.api.common.v1.RetryPolicy\x12\x15\n\rcron_schedule\x18\x0e \x01(\t\x12T\n\x07headers\x18\x0f \x03(\x0b\x32\x43.coresdk.workflow_commands.StartChildWorkflowExecution.HeadersEntry\x12N\n\x04memo\x18\x10 \x03(\x0b\x32@.coresdk.workflow_commands.StartChildWorkflowExecution.MemoEntry\x12g\n\x11search_attributes\x18\x11 \x03(\x0b\x32L.coresdk.workflow_commands.StartChildWorkflowExecution.SearchAttributesEntry\x12P\n\x11\x63\x61ncellation_type\x18\x12 \x01(\x0e\x32\x35.coresdk.child_workflow.ChildWorkflowCancellationType\x12;\n\x11versioning_intent\x18\x13 \x01(\x0e\x32 .coresdk.common.VersioningIntent\x1aO\n\x0cHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01\x1aL\n\tMemoEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01\x1aX\n\x15SearchAttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01":\n\x1c\x43\x61ncelChildWorkflowExecution\x12\x1a\n\x12\x63hild_workflow_seq\x18\x01 \x01(\r"\xa7\x01\n&RequestCancelExternalWorkflowExecution\x12\x0b\n\x03seq\x18\x01 \x01(\r\x12I\n\x12workflow_execution\x18\x02 \x01(\x0b\x32+.coresdk.common.NamespacedWorkflowExecutionH\x00\x12\x1b\n\x11\x63hild_workflow_id\x18\x03 \x01(\tH\x00\x42\x08\n\x06target"\x8f\x03\n\x1fSignalExternalWorkflowExecution\x12\x0b\n\x03seq\x18\x01 \x01(\r\x12I\n\x12workflow_execution\x18\x02 \x01(\x0b\x32+.coresdk.common.NamespacedWorkflowExecutionH\x00\x12\x1b\n\x11\x63hild_workflow_id\x18\x03 \x01(\tH\x00\x12\x13\n\x0bsignal_name\x18\x04 \x01(\t\x12-\n\x04\x61rgs\x18\x05 \x03(\x0b\x32\x1f.temporal.api.common.v1.Payload\x12X\n\x07headers\x18\x06 \x03(\x0b\x32G.coresdk.workflow_commands.SignalExternalWorkflowExecution.HeadersEntry\x1aO\n\x0cHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01\x42\x08\n\x06target"#\n\x14\x43\x61ncelSignalWorkflow\x12\x0b\n\x03seq\x18\x01 \x01(\r"\xe6\x01\n\x1eUpsertWorkflowSearchAttributes\x12j\n\x11search_attributes\x18\x01 \x03(\x0b\x32O.coresdk.workflow_commands.UpsertWorkflowSearchAttributes.SearchAttributesEntry\x1aX\n\x15SearchAttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12.\n\x05value\x18\x02 \x01(\x0b\x32\x1f.temporal.api.common.v1.Payload:\x02\x38\x01"O\n\x18ModifyWorkflowProperties\x12\x33\n\rupserted_memo\x18\x01 \x01(\x0b\x32\x1c.temporal.api.common.v1.Memo"\xd2\x01\n\x0eUpdateResponse\x12\x1c\n\x14protocol_instance_id\x18\x01 \x01(\t\x12*\n\x08\x61\x63\x63\x65pted\x18\x02 \x01(\x0b\x32\x16.google.protobuf.EmptyH\x00\x12\x34\n\x08rejected\x18\x03 \x01(\x0b\x32 .temporal.api.failure.v1.FailureH\x00\x12\x34\n\tcompleted\x18\x04 \x01(\x0b\x32\x1f.temporal.api.common.v1.PayloadH\x00\x42\n\n\x08response*X\n\x18\x41\x63tivityCancellationType\x12\x0e\n\nTRY_CANCEL\x10\x00\x12\x1f\n\x1bWAIT_CANCELLATION_COMPLETED\x10\x01\x12\x0b\n\x07\x41\x42\x41NDON\x10\x02\x42,\xea\x02)Temporalio::Bridge::Api::WorkflowCommandsb\x06proto3' ) _ACTIVITYCANCELLATIONTYPE = DESCRIPTOR.enum_types_by_name["ActivityCancellationType"] @@ -109,6 +110,7 @@ _UPSERTWORKFLOWSEARCHATTRIBUTES.nested_types_by_name["SearchAttributesEntry"] ) _MODIFYWORKFLOWPROPERTIES = DESCRIPTOR.message_types_by_name["ModifyWorkflowProperties"] +_UPDATERESPONSE = DESCRIPTOR.message_types_by_name["UpdateResponse"] WorkflowCommand = _reflection.GeneratedProtocolMessageType( "WorkflowCommand", (_message.Message,), @@ -440,6 +442,17 @@ ) _sym_db.RegisterMessage(ModifyWorkflowProperties) +UpdateResponse = _reflection.GeneratedProtocolMessageType( + "UpdateResponse", + (_message.Message,), + { + "DESCRIPTOR": _UPDATERESPONSE, + "__module__": "temporal.sdk.core.workflow_commands.workflow_commands_pb2" + # @@protoc_insertion_point(class_scope:coresdk.workflow_commands.UpdateResponse) + }, +) +_sym_db.RegisterMessage(UpdateResponse) + if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None DESCRIPTOR._serialized_options = ( @@ -465,68 +478,70 @@ _SIGNALEXTERNALWORKFLOWEXECUTION_HEADERSENTRY._serialized_options = b"8\001" _UPSERTWORKFLOWSEARCHATTRIBUTES_SEARCHATTRIBUTESENTRY._options = None _UPSERTWORKFLOWSEARCHATTRIBUTES_SEARCHATTRIBUTESENTRY._serialized_options = b"8\001" - _ACTIVITYCANCELLATIONTYPE._serialized_start = 7388 - _ACTIVITYCANCELLATIONTYPE._serialized_end = 7476 - _WORKFLOWCOMMAND._serialized_start = 365 - _WORKFLOWCOMMAND._serialized_end = 2073 - _STARTTIMER._serialized_start = 2075 - _STARTTIMER._serialized_end = 2158 - _CANCELTIMER._serialized_start = 2160 - _CANCELTIMER._serialized_end = 2186 - _SCHEDULEACTIVITY._serialized_start = 2189 - _SCHEDULEACTIVITY._serialized_end = 2961 - _SCHEDULEACTIVITY_HEADERSENTRY._serialized_start = 2882 - _SCHEDULEACTIVITY_HEADERSENTRY._serialized_end = 2961 - _SCHEDULELOCALACTIVITY._serialized_start = 2964 - _SCHEDULELOCALACTIVITY._serialized_end = 3714 - _SCHEDULELOCALACTIVITY_HEADERSENTRY._serialized_start = 2882 - _SCHEDULELOCALACTIVITY_HEADERSENTRY._serialized_end = 2961 - _REQUESTCANCELACTIVITY._serialized_start = 3716 - _REQUESTCANCELACTIVITY._serialized_end = 3752 - _REQUESTCANCELLOCALACTIVITY._serialized_start = 3754 - _REQUESTCANCELLOCALACTIVITY._serialized_end = 3795 - _QUERYRESULT._serialized_start = 3798 - _QUERYRESULT._serialized_end = 3954 - _QUERYSUCCESS._serialized_start = 3956 - _QUERYSUCCESS._serialized_end = 4021 - _COMPLETEWORKFLOWEXECUTION._serialized_start = 4023 - _COMPLETEWORKFLOWEXECUTION._serialized_end = 4099 - _FAILWORKFLOWEXECUTION._serialized_start = 4101 - _FAILWORKFLOWEXECUTION._serialized_end = 4175 - _CONTINUEASNEWWORKFLOWEXECUTION._serialized_start = 4178 - _CONTINUEASNEWWORKFLOWEXECUTION._serialized_end = 5069 - _CONTINUEASNEWWORKFLOWEXECUTION_MEMOENTRY._serialized_start = 4822 - _CONTINUEASNEWWORKFLOWEXECUTION_MEMOENTRY._serialized_end = 4898 - _CONTINUEASNEWWORKFLOWEXECUTION_HEADERSENTRY._serialized_start = 2882 - _CONTINUEASNEWWORKFLOWEXECUTION_HEADERSENTRY._serialized_end = 2961 - _CONTINUEASNEWWORKFLOWEXECUTION_SEARCHATTRIBUTESENTRY._serialized_start = 4981 - _CONTINUEASNEWWORKFLOWEXECUTION_SEARCHATTRIBUTESENTRY._serialized_end = 5069 - _CANCELWORKFLOWEXECUTION._serialized_start = 5071 - _CANCELWORKFLOWEXECUTION._serialized_end = 5096 - _SETPATCHMARKER._serialized_start = 5098 - _SETPATCHMARKER._serialized_end = 5152 - _STARTCHILDWORKFLOWEXECUTION._serialized_start = 5155 - _STARTCHILDWORKFLOWEXECUTION._serialized_end = 6403 - _STARTCHILDWORKFLOWEXECUTION_HEADERSENTRY._serialized_start = 2882 - _STARTCHILDWORKFLOWEXECUTION_HEADERSENTRY._serialized_end = 2961 - _STARTCHILDWORKFLOWEXECUTION_MEMOENTRY._serialized_start = 4822 - _STARTCHILDWORKFLOWEXECUTION_MEMOENTRY._serialized_end = 4898 - _STARTCHILDWORKFLOWEXECUTION_SEARCHATTRIBUTESENTRY._serialized_start = 4981 - _STARTCHILDWORKFLOWEXECUTION_SEARCHATTRIBUTESENTRY._serialized_end = 5069 - _CANCELCHILDWORKFLOWEXECUTION._serialized_start = 6405 - _CANCELCHILDWORKFLOWEXECUTION._serialized_end = 6463 - _REQUESTCANCELEXTERNALWORKFLOWEXECUTION._serialized_start = 6466 - _REQUESTCANCELEXTERNALWORKFLOWEXECUTION._serialized_end = 6633 - _SIGNALEXTERNALWORKFLOWEXECUTION._serialized_start = 6636 - _SIGNALEXTERNALWORKFLOWEXECUTION._serialized_end = 7035 - _SIGNALEXTERNALWORKFLOWEXECUTION_HEADERSENTRY._serialized_start = 2882 - _SIGNALEXTERNALWORKFLOWEXECUTION_HEADERSENTRY._serialized_end = 2961 - _CANCELSIGNALWORKFLOW._serialized_start = 7037 - _CANCELSIGNALWORKFLOW._serialized_end = 7072 - _UPSERTWORKFLOWSEARCHATTRIBUTES._serialized_start = 7075 - _UPSERTWORKFLOWSEARCHATTRIBUTES._serialized_end = 7305 - _UPSERTWORKFLOWSEARCHATTRIBUTES_SEARCHATTRIBUTESENTRY._serialized_start = 4981 - _UPSERTWORKFLOWSEARCHATTRIBUTES_SEARCHATTRIBUTESENTRY._serialized_end = 5069 - _MODIFYWORKFLOWPROPERTIES._serialized_start = 7307 - _MODIFYWORKFLOWPROPERTIES._serialized_end = 7386 + _ACTIVITYCANCELLATIONTYPE._serialized_start = 7700 + _ACTIVITYCANCELLATIONTYPE._serialized_end = 7788 + _WORKFLOWCOMMAND._serialized_start = 394 + _WORKFLOWCOMMAND._serialized_end = 2172 + _STARTTIMER._serialized_start = 2174 + _STARTTIMER._serialized_end = 2257 + _CANCELTIMER._serialized_start = 2259 + _CANCELTIMER._serialized_end = 2285 + _SCHEDULEACTIVITY._serialized_start = 2288 + _SCHEDULEACTIVITY._serialized_end = 3060 + _SCHEDULEACTIVITY_HEADERSENTRY._serialized_start = 2981 + _SCHEDULEACTIVITY_HEADERSENTRY._serialized_end = 3060 + _SCHEDULELOCALACTIVITY._serialized_start = 3063 + _SCHEDULELOCALACTIVITY._serialized_end = 3813 + _SCHEDULELOCALACTIVITY_HEADERSENTRY._serialized_start = 2981 + _SCHEDULELOCALACTIVITY_HEADERSENTRY._serialized_end = 3060 + _REQUESTCANCELACTIVITY._serialized_start = 3815 + _REQUESTCANCELACTIVITY._serialized_end = 3851 + _REQUESTCANCELLOCALACTIVITY._serialized_start = 3853 + _REQUESTCANCELLOCALACTIVITY._serialized_end = 3894 + _QUERYRESULT._serialized_start = 3897 + _QUERYRESULT._serialized_end = 4053 + _QUERYSUCCESS._serialized_start = 4055 + _QUERYSUCCESS._serialized_end = 4120 + _COMPLETEWORKFLOWEXECUTION._serialized_start = 4122 + _COMPLETEWORKFLOWEXECUTION._serialized_end = 4198 + _FAILWORKFLOWEXECUTION._serialized_start = 4200 + _FAILWORKFLOWEXECUTION._serialized_end = 4274 + _CONTINUEASNEWWORKFLOWEXECUTION._serialized_start = 4277 + _CONTINUEASNEWWORKFLOWEXECUTION._serialized_end = 5168 + _CONTINUEASNEWWORKFLOWEXECUTION_MEMOENTRY._serialized_start = 4921 + _CONTINUEASNEWWORKFLOWEXECUTION_MEMOENTRY._serialized_end = 4997 + _CONTINUEASNEWWORKFLOWEXECUTION_HEADERSENTRY._serialized_start = 2981 + _CONTINUEASNEWWORKFLOWEXECUTION_HEADERSENTRY._serialized_end = 3060 + _CONTINUEASNEWWORKFLOWEXECUTION_SEARCHATTRIBUTESENTRY._serialized_start = 5080 + _CONTINUEASNEWWORKFLOWEXECUTION_SEARCHATTRIBUTESENTRY._serialized_end = 5168 + _CANCELWORKFLOWEXECUTION._serialized_start = 5170 + _CANCELWORKFLOWEXECUTION._serialized_end = 5195 + _SETPATCHMARKER._serialized_start = 5197 + _SETPATCHMARKER._serialized_end = 5251 + _STARTCHILDWORKFLOWEXECUTION._serialized_start = 5254 + _STARTCHILDWORKFLOWEXECUTION._serialized_end = 6502 + _STARTCHILDWORKFLOWEXECUTION_HEADERSENTRY._serialized_start = 2981 + _STARTCHILDWORKFLOWEXECUTION_HEADERSENTRY._serialized_end = 3060 + _STARTCHILDWORKFLOWEXECUTION_MEMOENTRY._serialized_start = 4921 + _STARTCHILDWORKFLOWEXECUTION_MEMOENTRY._serialized_end = 4997 + _STARTCHILDWORKFLOWEXECUTION_SEARCHATTRIBUTESENTRY._serialized_start = 5080 + _STARTCHILDWORKFLOWEXECUTION_SEARCHATTRIBUTESENTRY._serialized_end = 5168 + _CANCELCHILDWORKFLOWEXECUTION._serialized_start = 6504 + _CANCELCHILDWORKFLOWEXECUTION._serialized_end = 6562 + _REQUESTCANCELEXTERNALWORKFLOWEXECUTION._serialized_start = 6565 + _REQUESTCANCELEXTERNALWORKFLOWEXECUTION._serialized_end = 6732 + _SIGNALEXTERNALWORKFLOWEXECUTION._serialized_start = 6735 + _SIGNALEXTERNALWORKFLOWEXECUTION._serialized_end = 7134 + _SIGNALEXTERNALWORKFLOWEXECUTION_HEADERSENTRY._serialized_start = 2981 + _SIGNALEXTERNALWORKFLOWEXECUTION_HEADERSENTRY._serialized_end = 3060 + _CANCELSIGNALWORKFLOW._serialized_start = 7136 + _CANCELSIGNALWORKFLOW._serialized_end = 7171 + _UPSERTWORKFLOWSEARCHATTRIBUTES._serialized_start = 7174 + _UPSERTWORKFLOWSEARCHATTRIBUTES._serialized_end = 7404 + _UPSERTWORKFLOWSEARCHATTRIBUTES_SEARCHATTRIBUTESENTRY._serialized_start = 5080 + _UPSERTWORKFLOWSEARCHATTRIBUTES_SEARCHATTRIBUTESENTRY._serialized_end = 5168 + _MODIFYWORKFLOWPROPERTIES._serialized_start = 7406 + _MODIFYWORKFLOWPROPERTIES._serialized_end = 7485 + _UPDATERESPONSE._serialized_start = 7488 + _UPDATERESPONSE._serialized_end = 7698 # @@protoc_insertion_point(module_scope) diff --git a/temporalio/bridge/proto/workflow_commands/workflow_commands_pb2.pyi b/temporalio/bridge/proto/workflow_commands/workflow_commands_pb2.pyi index ebd97abe..3bea766b 100644 --- a/temporalio/bridge/proto/workflow_commands/workflow_commands_pb2.pyi +++ b/temporalio/bridge/proto/workflow_commands/workflow_commands_pb2.pyi @@ -10,6 +10,7 @@ import builtins import collections.abc import google.protobuf.descriptor import google.protobuf.duration_pb2 +import google.protobuf.empty_pb2 import google.protobuf.internal.containers import google.protobuf.internal.enum_type_wrapper import google.protobuf.message @@ -91,6 +92,7 @@ class WorkflowCommand(google.protobuf.message.Message): REQUEST_CANCEL_LOCAL_ACTIVITY_FIELD_NUMBER: builtins.int UPSERT_WORKFLOW_SEARCH_ATTRIBUTES_FIELD_NUMBER: builtins.int MODIFY_WORKFLOW_PROPERTIES_FIELD_NUMBER: builtins.int + UPDATE_RESPONSE_FIELD_NUMBER: builtins.int @property def start_timer(self) -> global___StartTimer: ... @property @@ -141,6 +143,8 @@ class WorkflowCommand(google.protobuf.message.Message): ) -> global___UpsertWorkflowSearchAttributes: ... @property def modify_workflow_properties(self) -> global___ModifyWorkflowProperties: ... + @property + def update_response(self) -> global___UpdateResponse: ... def __init__( self, *, @@ -169,6 +173,7 @@ class WorkflowCommand(google.protobuf.message.Message): upsert_workflow_search_attributes: global___UpsertWorkflowSearchAttributes | None = ..., modify_workflow_properties: global___ModifyWorkflowProperties | None = ..., + update_response: global___UpdateResponse | None = ..., ) -> None: ... def HasField( self, @@ -209,6 +214,8 @@ class WorkflowCommand(google.protobuf.message.Message): b"start_child_workflow_execution", "start_timer", b"start_timer", + "update_response", + b"update_response", "upsert_workflow_search_attributes", b"upsert_workflow_search_attributes", "variant", @@ -254,6 +261,8 @@ class WorkflowCommand(google.protobuf.message.Message): b"start_child_workflow_execution", "start_timer", b"start_timer", + "update_response", + b"update_response", "upsert_workflow_search_attributes", b"upsert_workflow_search_attributes", "variant", @@ -283,6 +292,7 @@ class WorkflowCommand(google.protobuf.message.Message): "request_cancel_local_activity", "upsert_workflow_search_attributes", "modify_workflow_properties", + "update_response", ] | None ): ... @@ -1580,3 +1590,79 @@ class ModifyWorkflowProperties(google.protobuf.message.Message): ) -> None: ... global___ModifyWorkflowProperties = ModifyWorkflowProperties + +class UpdateResponse(google.protobuf.message.Message): + """A reply to a `DoUpdate` job - lang must run the update's validator if told to, and then + immediately run the handler, if the update was accepted. + + There must always be an accepted or rejected response immediately, in the same activation as + this job, to indicate the result of the validator. Accepted for ran and accepted or skipped, or + rejected for rejected. + + Then, in the same or any subsequent activation, after the update handler has completed, respond + with completed or rejected as appropriate for the result of the handler. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PROTOCOL_INSTANCE_ID_FIELD_NUMBER: builtins.int + ACCEPTED_FIELD_NUMBER: builtins.int + REJECTED_FIELD_NUMBER: builtins.int + COMPLETED_FIELD_NUMBER: builtins.int + protocol_instance_id: builtins.str + """The protocol message instance ID""" + @property + def accepted(self) -> google.protobuf.empty_pb2.Empty: + """Must be sent if the update's validator has passed (or lang was not asked to run it, and + thus should be considered already-accepted, allowing lang to always send the same + sequence on replay). + """ + @property + def rejected(self) -> temporalio.api.failure.v1.message_pb2.Failure: + """Must be sent if the update's validator does not pass, or after acceptance if the update + handler fails. + """ + @property + def completed(self) -> temporalio.api.common.v1.message_pb2.Payload: + """Must be sent once the update handler completes successfully.""" + def __init__( + self, + *, + protocol_instance_id: builtins.str = ..., + accepted: google.protobuf.empty_pb2.Empty | None = ..., + rejected: temporalio.api.failure.v1.message_pb2.Failure | None = ..., + completed: temporalio.api.common.v1.message_pb2.Payload | None = ..., + ) -> None: ... + def HasField( + self, + field_name: typing_extensions.Literal[ + "accepted", + b"accepted", + "completed", + b"completed", + "rejected", + b"rejected", + "response", + b"response", + ], + ) -> builtins.bool: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "accepted", + b"accepted", + "completed", + b"completed", + "protocol_instance_id", + b"protocol_instance_id", + "rejected", + b"rejected", + "response", + b"response", + ], + ) -> None: ... + def WhichOneof( + self, oneof_group: typing_extensions.Literal["response", b"response"] + ) -> typing_extensions.Literal["accepted", "rejected", "completed"] | None: ... + +global___UpdateResponse = UpdateResponse diff --git a/temporalio/bridge/sdk-core b/temporalio/bridge/sdk-core index 617612aa..7545ad12 160000 --- a/temporalio/bridge/sdk-core +++ b/temporalio/bridge/sdk-core @@ -1 +1 @@ -Subproject commit 617612aa419d687aebabcf0258ac86f5c36df189 +Subproject commit 7545ad126166412d282c8dfe43d1650d607867dd diff --git a/temporalio/bridge/src/worker.rs b/temporalio/bridge/src/worker.rs index 5d8be94a..151d7ce9 100644 --- a/temporalio/bridge/src/worker.rs +++ b/temporalio/bridge/src/worker.rs @@ -5,7 +5,7 @@ use pyo3::types::{PyBytes, PyTuple}; use std::sync::Arc; use std::time::Duration; use temporal_sdk_core::api::errors::{PollActivityError, PollWfError}; -use temporal_sdk_core::replay::HistoryForReplay; +use temporal_sdk_core::replay::{HistoryForReplay, ReplayWorkerInput}; use temporal_sdk_core_api::Worker; use temporal_sdk_core_protos::coresdk::workflow_completion::WorkflowActivationCompletion; use temporal_sdk_core_protos::coresdk::{ActivityHeartbeat, ActivityTaskCompletion}; @@ -85,9 +85,9 @@ pub fn new_replay_worker<'a>( let (history_pusher, stream) = HistoryPusher::new(runtime_ref.runtime.clone()); let worker = WorkerRef { worker: Some(Arc::new( - temporal_sdk_core::init_replay_worker(config, stream).map_err(|err| { - PyValueError::new_err(format!("Failed creating replay worker: {}", err)) - })?, + temporal_sdk_core::init_replay_worker(ReplayWorkerInput::new(config, stream)).map_err( + |err| PyValueError::new_err(format!("Failed creating replay worker: {}", err)), + )?, )), runtime: runtime_ref.runtime.clone(), }; diff --git a/temporalio/client.py b/temporalio/client.py index aceb3301..3ee53778 100644 --- a/temporalio/client.py +++ b/temporalio/client.py @@ -2,11 +2,13 @@ from __future__ import annotations +import asyncio import copy import dataclasses import inspect import json import re +import sys import uuid import warnings from abc import ABC, abstractmethod @@ -43,6 +45,7 @@ import temporalio.api.history.v1 import temporalio.api.schedule.v1 import temporalio.api.taskqueue.v1 +import temporalio.api.update.v1 import temporalio.api.workflow.v1 import temporalio.api.workflowservice.v1 import temporalio.common @@ -1627,6 +1630,259 @@ async def terminate( ) ) + # Overload for no-param update + @overload + async def execute_update( + self, + update: temporalio.workflow.UpdateMethodMultiParam[[SelfType], LocalReturnType], + *, + id: Optional[str] = None, + rpc_metadata: Mapping[str, str] = {}, + rpc_timeout: Optional[timedelta] = None, + ) -> LocalReturnType: + ... + + # Overload for single-param update + @overload + async def execute_update( + self, + update: temporalio.workflow.UpdateMethodMultiParam[ + [SelfType, ParamType], LocalReturnType + ], + arg: ParamType, + *, + id: Optional[str] = None, + rpc_metadata: Mapping[str, str] = {}, + rpc_timeout: Optional[timedelta] = None, + ) -> LocalReturnType: + ... + + # Overload for multi-param update + @overload + async def execute_update( + self, + update: temporalio.workflow.UpdateMethodMultiParam[ + MultiParamSpec, LocalReturnType + ], + *, + args: MultiParamSpec.args, + id: Optional[str] = None, + rpc_metadata: Mapping[str, str] = {}, + rpc_timeout: Optional[timedelta] = None, + ) -> LocalReturnType: + ... + + # Overload for string-name update + @overload + async def execute_update( + self, + update: str, + arg: Any = temporalio.common._arg_unset, + *, + args: Sequence[Any] = [], + id: Optional[str] = None, + result_type: Optional[Type] = None, + rpc_metadata: Mapping[str, str] = {}, + rpc_timeout: Optional[timedelta] = None, + ) -> Any: + ... + + async def execute_update( + self, + update: Union[str, Callable], + arg: Any = temporalio.common._arg_unset, + *, + args: Sequence[Any] = [], + id: Optional[str] = None, + result_type: Optional[Type] = None, + rpc_metadata: Mapping[str, str] = {}, + rpc_timeout: Optional[timedelta] = None, + ) -> Any: + """Send an update request to the workflow and wait for it to complete. + + This will target the workflow with :py:attr:`run_id` if present. To use a + different run ID, create a new handle with via :py:meth:`Client.get_workflow_handle`. + + .. warning:: + This API is experimental + + .. warning:: + WorkflowHandles created as a result of :py:meth:`Client.start_workflow` will + send updates to the latest workflow with the same workflow ID even if it is + unrelated to the started workflow. + + Args: + update: Update function or name on the workflow. + arg: Single argument to the update. + args: Multiple arguments to the update. Cannot be set if arg is. + id: ID of the update. If not set, the server will set a UUID as the ID. + result_type: For string updates, this can set the specific result + type hint to deserialize into. + rpc_metadata: Headers used on the RPC call. Keys here override + client-level RPC metadata keys. + rpc_timeout: Optional RPC deadline to set for the RPC call. + + Raises: + WorkflowUpdateFailedError: If the update failed + RPCError: There was some issue sending the update to the workflow. + """ + handle = await self._start_update( + update, + arg, + args=args, + id=id, + wait_for_stage=temporalio.api.enums.v1.UpdateWorkflowExecutionLifecycleStage.UPDATE_WORKFLOW_EXECUTION_LIFECYCLE_STAGE_COMPLETED, + result_type=result_type, + rpc_metadata=rpc_metadata, + rpc_timeout=rpc_timeout, + ) + return await handle.result() + + # Overload for no-param start update + @overload + async def start_update( + self, + update: temporalio.workflow.UpdateMethodMultiParam[[SelfType], LocalReturnType], + *, + id: Optional[str] = None, + rpc_metadata: Mapping[str, str] = {}, + rpc_timeout: Optional[timedelta] = None, + ) -> WorkflowUpdateHandle[LocalReturnType]: + ... + + # Overload for single-param start update + @overload + async def start_update( + self, + update: temporalio.workflow.UpdateMethodMultiParam[ + [SelfType, ParamType], LocalReturnType + ], + arg: ParamType, + *, + id: Optional[str] = None, + rpc_metadata: Mapping[str, str] = {}, + rpc_timeout: Optional[timedelta] = None, + ) -> WorkflowUpdateHandle[LocalReturnType]: + ... + + # Overload for multi-param start update + @overload + async def start_update( + self, + update: temporalio.workflow.UpdateMethodMultiParam[ + MultiParamSpec, LocalReturnType + ], + *, + args: MultiParamSpec.args, + id: Optional[str] = None, + rpc_metadata: Mapping[str, str] = {}, + rpc_timeout: Optional[timedelta] = None, + ) -> WorkflowUpdateHandle[LocalReturnType]: + ... + + # Overload for string-name start update + @overload + async def start_update( + self, + update: str, + arg: Any = temporalio.common._arg_unset, + *, + args: Sequence[Any] = [], + id: Optional[str] = None, + result_type: Optional[Type] = None, + rpc_metadata: Mapping[str, str] = {}, + rpc_timeout: Optional[timedelta] = None, + ) -> WorkflowUpdateHandle[Any]: + ... + + async def start_update( + self, + update: Union[str, Callable], + arg: Any = temporalio.common._arg_unset, + *, + args: Sequence[Any] = [], + id: Optional[str] = None, + result_type: Optional[Type] = None, + rpc_metadata: Mapping[str, str] = {}, + rpc_timeout: Optional[timedelta] = None, + ) -> WorkflowUpdateHandle[Any]: + """Send an update request to the workflow and return a handle to it. + + This will target the workflow with :py:attr:`run_id` if present. To use a + different run ID, create a new handle with via :py:meth:`Client.get_workflow_handle`. + + .. warning:: + This API is experimental + + .. warning:: + WorkflowHandles created as a result of :py:meth:`Client.start_workflow` will + send updates to the latest workflow with the same workflow ID even if it is + unrelated to the started workflow. + + Args: + update: Update function or name on the workflow. + arg: Single argument to the update. + args: Multiple arguments to the update. Cannot be set if arg is. + id: ID of the update. If not set, the server will set a UUID as the ID. + result_type: For string updates, this can set the specific result + type hint to deserialize into. + rpc_metadata: Headers used on the RPC call. Keys here override + client-level RPC metadata keys. + rpc_timeout: Optional RPC deadline to set for the RPC call. + + Raises: + RPCError: There was some issue sending the update to the workflow. + """ + return await self._start_update( + update, + arg, + args=args, + id=id, + wait_for_stage=temporalio.api.enums.v1.UpdateWorkflowExecutionLifecycleStage.UPDATE_WORKFLOW_EXECUTION_LIFECYCLE_STAGE_ACCEPTED, + result_type=result_type, + rpc_metadata=rpc_metadata, + rpc_timeout=rpc_timeout, + ) + + async def _start_update( + self, + update: Union[str, Callable], + arg: Any = temporalio.common._arg_unset, + *, + args: Sequence[Any] = [], + id: Optional[str] = None, + wait_for_stage: temporalio.api.enums.v1.UpdateWorkflowExecutionLifecycleStage.ValueType = temporalio.api.enums.v1.UpdateWorkflowExecutionLifecycleStage.UPDATE_WORKFLOW_EXECUTION_LIFECYCLE_STAGE_ADMITTED, + result_type: Optional[Type] = None, + rpc_metadata: Mapping[str, str] = {}, + rpc_timeout: Optional[timedelta] = None, + ) -> WorkflowUpdateHandle[Any]: + update_name: str + ret_type = result_type + if isinstance(update, temporalio.workflow.UpdateMethodMultiParam): + defn = update._defn + if not defn.name: + raise RuntimeError("Cannot invoke dynamic update definition") + # TODO(cretz): Check count/type of args at runtime? + update_name = defn.name + ret_type = defn.ret_type + else: + update_name = str(update) + + return await self._client._impl.start_workflow_update( + StartWorkflowUpdateInput( + id=self._id, + run_id=self._run_id, + update_id=id, + update=update_name, + args=temporalio.common._arg_or_args(arg, args), + headers={}, + ret_type=ret_type, + rpc_metadata=rpc_metadata, + rpc_timeout=rpc_timeout, + wait_for_stage=wait_for_stage, + ) + ) + @dataclass(frozen=True) class AsyncActivityIDReference: @@ -2711,7 +2967,9 @@ class ScheduleActionStartWorkflow(ScheduleAction): headers: Optional[Mapping[str, temporalio.api.common.v1.Payload]] = None @staticmethod - def _from_proto(info: temporalio.api.workflow.v1.NewWorkflowExecutionInfo) -> ScheduleActionStartWorkflow: # type: ignore[override] + def _from_proto( + info: temporalio.api.workflow.v1.NewWorkflowExecutionInfo, # type: ignore[override] + ) -> ScheduleActionStartWorkflow: return ScheduleActionStartWorkflow("", raw_info=info) # Overload for no-param workflow @@ -3679,6 +3937,89 @@ async def __anext__(self) -> ScheduleListDescription: return ret +class WorkflowUpdateHandle(Generic[LocalReturnType]): + """Handle for a workflow update execution request.""" + + def __init__( + self, + client: Client, + id: str, + workflow_id: str, + *, + workflow_run_id: Optional[str] = None, + result_type: Optional[Type] = None, + ): + """Create a workflow update handle. + + Users should not create this directly, but rather use + :py:meth:`Client.start_workflow_update`. + """ + self._client = client + self._id = id + self._workflow_id = workflow_id + self._workflow_run_id = workflow_run_id + self._result_type = result_type + self._known_outcome: Optional[temporalio.api.update.v1.Outcome] = None + + @property + def id(self) -> str: + """ID of this Update request.""" + return self._id + + @property + def workflow_id(self) -> str: + """The ID of the Workflow targeted by this Update.""" + return self._workflow_id + + @property + def workflow_run_id(self) -> Optional[str]: + """If specified, the specific run of the Workflow targeted by this Update.""" + return self._workflow_run_id + + async def result( + self, + *, + timeout: Optional[timedelta] = None, + rpc_metadata: Mapping[str, str] = {}, + rpc_timeout: Optional[timedelta] = None, + ) -> LocalReturnType: + """Wait for and return the result of the update. The result may already be known in which case no network call + is made. Otherwise the result will be polled for until returned, or until the provided timeout is reached, if + specified. + + Args: + timeout: Optional timeout specifying maximum wait time for the result. + rpc_metadata: Headers used on the RPC call. Keys here override client-level RPC metadata keys. + rpc_timeout: Optional RPC deadline to set for the RPC call. If this elapses, the poll is retried until the + overall timeout has been reached. + + Raises: + WorkflowUpdateFailedError: If the update failed + TimeoutError: The specified timeout was reached when waiting for the update result. + RPCError: Update result could not be fetched for some other reason. + """ + if self._known_outcome is not None: + outcome = self._known_outcome + return await _update_outcome_to_result( + outcome, + self.id, + self._client.data_converter, + self._result_type, + ) + + return await self._client._impl.poll_workflow_update( + PollWorkflowUpdateInput( + self.workflow_id, + self.workflow_run_id, + self.id, + timeout, + self._result_type, + rpc_metadata, + rpc_timeout, + ) + ) + + class WorkflowFailureError(temporalio.exceptions.TemporalError): """Error that occurs when a workflow is unsuccessful.""" @@ -3736,6 +4077,21 @@ def message(self) -> str: return self._message +class WorkflowUpdateFailedError(temporalio.exceptions.TemporalError): + """Error that occurs when an update fails.""" + + def __init__(self, cause: BaseException) -> None: + """Create workflow update failed error.""" + super().__init__("Workflow update failed") + self.__cause__ = cause + + @property + def cause(self) -> BaseException: + """Cause of the update failure.""" + assert self.__cause__ + return self.__cause__ + + class AsyncActivityCancelledError(temporalio.exceptions.TemporalError): """Error that occurs when async activity attempted heartbeat but was cancelled.""" @@ -3867,6 +4223,37 @@ class TerminateWorkflowInput: rpc_timeout: Optional[timedelta] +@dataclass +class StartWorkflowUpdateInput: + """Input for :py:meth:`OutboundInterceptor.start_workflow_update`.""" + + id: str + run_id: Optional[str] + update_id: Optional[str] + update: str + args: Sequence[Any] + wait_for_stage: Optional[ + temporalio.api.enums.v1.UpdateWorkflowExecutionLifecycleStage.ValueType + ] + headers: Mapping[str, temporalio.api.common.v1.Payload] + ret_type: Optional[Type] + rpc_metadata: Mapping[str, str] + rpc_timeout: Optional[timedelta] + + +@dataclass +class PollWorkflowUpdateInput: + """Input for :py:meth:`OutboundInterceptor.poll_workflow_update`.""" + + workflow_id: str + run_id: Optional[str] + update_id: str + timeout: Optional[timedelta] + ret_type: Optional[Type] + rpc_metadata: Mapping[str, str] + rpc_timeout: Optional[timedelta] + + @dataclass class HeartbeatAsyncActivityInput: """Input for :py:meth:`OutboundInterceptor.heartbeat_async_activity`.""" @@ -4111,6 +4498,16 @@ async def terminate_workflow(self, input: TerminateWorkflowInput) -> None: """Called for every :py:meth:`WorkflowHandle.terminate` call.""" await self.next.terminate_workflow(input) + async def start_workflow_update( + self, input: StartWorkflowUpdateInput + ) -> WorkflowUpdateHandle[Any]: + """Called for every :py:meth:`WorkflowHandle.update` and :py:meth:`WorkflowHandle.start_update` call.""" + return await self.next.start_workflow_update(input) + + async def poll_workflow_update(self, input: PollWorkflowUpdateInput) -> Any: + """May be called when calling :py:meth:`WorkflowUpdateHandle.result`.""" + return await self.next.poll_workflow_update(input) + ### Async activity calls async def heartbeat_async_activity( @@ -4435,6 +4832,101 @@ async def terminate_workflow(self, input: TerminateWorkflowInput) -> None: req, retry=True, metadata=input.rpc_metadata, timeout=input.rpc_timeout ) + async def start_workflow_update( + self, input: StartWorkflowUpdateInput + ) -> WorkflowUpdateHandle[Any]: + wait_policy = ( + temporalio.api.update.v1.WaitPolicy(lifecycle_stage=input.wait_for_stage) + if input.wait_for_stage is not None + else None + ) + req = temporalio.api.workflowservice.v1.UpdateWorkflowExecutionRequest( + namespace=self._client.namespace, + workflow_execution=temporalio.api.common.v1.WorkflowExecution( + workflow_id=input.id, + run_id=input.run_id or "", + ), + request=temporalio.api.update.v1.Request( + meta=temporalio.api.update.v1.Meta( + update_id=input.update_id or "", + identity=self._client.identity, + ), + input=temporalio.api.update.v1.Input( + name=input.update, + ), + ), + wait_policy=wait_policy, + ) + if input.args: + req.request.input.args.payloads.extend( + await self._client.data_converter.encode(input.args) + ) + if input.headers is not None: + temporalio.common._apply_headers( + input.headers, req.request.input.header.fields + ) + try: + resp = await self._client.workflow_service.update_workflow_execution( + req, retry=True, metadata=input.rpc_metadata, timeout=input.rpc_timeout + ) + except RPCError as err: + raise + + determined_id = resp.update_ref.update_id + update_handle: WorkflowUpdateHandle[Any] = WorkflowUpdateHandle( + client=self._client, + id=determined_id, + workflow_id=input.id, + workflow_run_id=input.run_id, + result_type=input.ret_type, + ) + if resp.HasField("outcome"): + update_handle._known_outcome = resp.outcome + + return update_handle + + async def poll_workflow_update(self, input: PollWorkflowUpdateInput) -> Any: + req = temporalio.api.workflowservice.v1.PollWorkflowExecutionUpdateRequest( + namespace=self._client.namespace, + update_ref=temporalio.api.update.v1.UpdateRef( + workflow_execution=temporalio.api.common.v1.WorkflowExecution( + workflow_id=input.workflow_id, + run_id=input.run_id or "", + ), + update_id=input.update_id, + ), + identity=self._client.identity, + wait_policy=temporalio.api.update.v1.WaitPolicy( + lifecycle_stage=temporalio.api.enums.v1.UpdateWorkflowExecutionLifecycleStage.UPDATE_WORKFLOW_EXECUTION_LIFECYCLE_STAGE_COMPLETED + ), + ) + + async def poll_loop(): + # Continue polling as long as we have either an empty response, or an *rpc* timeout + while True: + try: + res = await self._client.workflow_service.poll_workflow_execution_update( + req, + retry=True, + metadata=input.rpc_metadata, + timeout=input.rpc_timeout, + ) + if res.HasField("outcome"): + return await _update_outcome_to_result( + res.outcome, + input.update_id, + self._client.data_converter, + input.ret_type, + ) + except RPCError as err: + if err.status != RPCStatusCode.DEADLINE_EXCEEDED: + raise + + # Wait for at most the *overall* timeout + return await asyncio.wait_for( + poll_loop(), input.timeout.total_seconds() if input.timeout else None + ) + ### Async activity calls async def heartbeat_async_activity( @@ -4963,6 +5455,27 @@ def _fix_history_enum(prefix: str, parent: Dict[str, Any], *attrs: str) -> None: _fix_history_enum(prefix, child_item, *attrs[1:]) +async def _update_outcome_to_result( + outcome: temporalio.api.update.v1.Outcome, + id: str, + converter: temporalio.converter.DataConverter, + rtype: Optional[Type], +) -> Any: + if outcome.HasField("failure"): + raise WorkflowUpdateFailedError( + await converter.decode_failure(outcome.failure), + ) + if not outcome.success.payloads: + return None + type_hints = [rtype] if rtype else None + results = await converter.decode(outcome.success.payloads, type_hints) + if not results: + return None + elif len(results) > 1: + warnings.warn(f"Expected single update result, got {len(results)}") + return results[0] + + @dataclass(frozen=True) class WorkerBuildIdVersionSets: """Represents the sets of compatible Build ID versions associated with some Task Queue, as diff --git a/temporalio/contrib/opentelemetry.py b/temporalio/contrib/opentelemetry.py index 2701afa1..3f292098 100644 --- a/temporalio/contrib/opentelemetry.py +++ b/temporalio/contrib/opentelemetry.py @@ -244,6 +244,17 @@ async def signal_workflow( ): return await super().signal_workflow(input) + async def start_workflow_update( + self, input: temporalio.client.StartWorkflowUpdateInput + ) -> temporalio.client.WorkflowUpdateHandle[Any]: + with self.root._start_as_current_span( + f"StartWorkflowUpdate:{input.update}", + attributes={"temporalWorkflowID": input.id}, + input=input, + kind=opentelemetry.trace.SpanKind.CLIENT, + ): + return await super().start_workflow_update(input) + class _TracingActivityInboundInterceptor(temporalio.worker.ActivityInboundInterceptor): def __init__( @@ -404,6 +415,46 @@ async def handle_query(self, input: temporalio.worker.HandleQueryInput) -> Any: finally: opentelemetry.context.detach(token) + def handle_update_validator( + self, input: temporalio.worker.HandleUpdateInput + ) -> None: + """Implementation of + :py:meth:`temporalio.worker.WorkflowInboundInterceptor.handle_update_validator`. + """ + link_context_header = input.headers.get(self.header_key) + link_context_carrier: Optional[_CarrierDict] = None + if link_context_header: + link_context_carrier = self.payload_converter.from_payloads( + [link_context_header] + )[0] + with self._top_level_workflow_context(success_is_complete=False): + self._completed_span( + f"ValidateUpdate:{input.update}", + link_context_carrier=link_context_carrier, + kind=opentelemetry.trace.SpanKind.SERVER, + ) + super().handle_update_validator(input) + + async def handle_update_handler( + self, input: temporalio.worker.HandleUpdateInput + ) -> Any: + """Implementation of + :py:meth:`temporalio.worker.WorkflowInboundInterceptor.handle_update_handler`. + """ + link_context_header = input.headers.get(self.header_key) + link_context_carrier: Optional[_CarrierDict] = None + if link_context_header: + link_context_carrier = self.payload_converter.from_payloads( + [link_context_header] + )[0] + with self._top_level_workflow_context(success_is_complete=False): + self._completed_span( + f"HandleUpdate:{input.update}", + link_context_carrier=link_context_carrier, + kind=opentelemetry.trace.SpanKind.SERVER, + ) + return await super().handle_update_handler(input) + def _load_workflow_context_carrier(self) -> Optional[_CarrierDict]: if self._workflow_context_carrier: return self._workflow_context_carrier diff --git a/temporalio/types.py b/temporalio/types.py index d6590338..c6528a84 100644 --- a/temporalio/types.py +++ b/temporalio/types.py @@ -12,6 +12,10 @@ LocalReturnType = TypeVar("LocalReturnType", covariant=True) CallableType = TypeVar("CallableType", bound=Callable[..., Any]) CallableAsyncType = TypeVar("CallableAsyncType", bound=Callable[..., Awaitable[Any]]) +CallableSyncOrAsyncType = TypeVar( + "CallableSyncOrAsyncType", + bound=Callable[..., Union[Any, Awaitable[Any]]], +) CallableSyncOrAsyncReturnNoneType = TypeVar( "CallableSyncOrAsyncReturnNoneType", bound=Callable[..., Union[None, Awaitable[None]]], diff --git a/temporalio/worker/__init__.py b/temporalio/worker/__init__.py index b7804e54..aa7936e6 100644 --- a/temporalio/worker/__init__.py +++ b/temporalio/worker/__init__.py @@ -9,6 +9,7 @@ ExecuteWorkflowInput, HandleQueryInput, HandleSignalInput, + HandleUpdateInput, Interceptor, SignalChildWorkflowInput, SignalExternalWorkflowInput, @@ -53,6 +54,7 @@ "ExecuteWorkflowInput", "HandleQueryInput", "HandleSignalInput", + "HandleUpdateInput", "SignalChildWorkflowInput", "SignalExternalWorkflowInput", "StartActivityInput", diff --git a/temporalio/worker/_interceptor.py b/temporalio/worker/_interceptor.py index 19283e01..af85161d 100644 --- a/temporalio/worker/_interceptor.py +++ b/temporalio/worker/_interceptor.py @@ -190,6 +190,18 @@ class HandleQueryInput: headers: Mapping[str, temporalio.api.common.v1.Payload] +@dataclass +class HandleUpdateInput: + """Input for :py:meth:`WorkflowInboundInterceptor.handle_update_validator` + and :py:meth:`WorkflowInboundInterceptor.handle_update_handler`. + """ + + id: str + update: str + args: Sequence[Any] + headers: Mapping[str, temporalio.api.common.v1.Payload] + + @dataclass class SignalChildWorkflowInput: """Input for :py:meth:`WorkflowOutboundInterceptor.signal_child_workflow`.""" @@ -314,6 +326,14 @@ async def handle_query(self, input: HandleQueryInput) -> Any: """Called to handle a query.""" return await self.next.handle_query(input) + def handle_update_validator(self, input: HandleUpdateInput) -> None: + """Called to handle an update's validation stage.""" + self.next.handle_update_validator(input) + + async def handle_update_handler(self, input: HandleUpdateInput) -> Any: + """Called to handle an update's handler.""" + return await self.next.handle_update_handler(input) + class WorkflowOutboundInterceptor: """Outbound interceptor to wrap calls made from within workflows. diff --git a/temporalio/worker/_workflow_instance.py b/temporalio/worker/_workflow_instance.py index d5afece7..939f68ae 100644 --- a/temporalio/worker/_workflow_instance.py +++ b/temporalio/worker/_workflow_instance.py @@ -37,6 +37,7 @@ cast, ) +import google.protobuf.empty_pb2 from typing_extensions import TypeAlias, TypedDict import temporalio.activity @@ -57,6 +58,7 @@ ExecuteWorkflowInput, HandleQueryInput, HandleSignalInput, + HandleUpdateInput, SignalChildWorkflowInput, SignalExternalWorkflowInput, StartActivityInput, @@ -209,10 +211,11 @@ def __init__(self, det: WorkflowInstanceDetails) -> None: # See https://bugs.python.org/issue21163 and others. self._tasks: Set[asyncio.Task] = set() - # We maintain signals and queries on this class since handlers can be + # We maintain signals, queries, and updates on this class since handlers can be # added during workflow execution self._signals = dict(self._defn.signals) self._queries = dict(self._defn.queries) + self._updates = dict(self._defn.updates) # Add stack trace handler # TODO(cretz): Is it ok that this can be forcefully overridden by the @@ -367,6 +370,8 @@ def _apply( ) -> None: if job.HasField("cancel_workflow"): self._apply_cancel_workflow(job.cancel_workflow) + elif job.HasField("do_update"): + self._apply_do_update(job.do_update) elif job.HasField("fire_timer"): self._apply_fire_timer(job.fire_timer) elif job.HasField("query_workflow"): @@ -414,6 +419,120 @@ def _apply_cancel_workflow( # this cancellation to the next iteration of the event loop. self.call_soon(self._primary_task.cancel) + def _apply_do_update( + self, job: temporalio.bridge.proto.workflow_activation.DoUpdate + ): + # Run the validator & handler in a task. Everything, including looking up the update definition, needs to be + # inside the task, since the update may not be defined until after we have started the workflow - for example + # if an update is in the first WFT & is also registered dynamically at the top of workflow code. + async def run_update() -> None: + command = self._add_command() + command.update_response.protocol_instance_id = job.protocol_instance_id + past_validation = False + try: + defn = self._updates.get(job.name) or self._updates.get(None) + if not defn: + known_updates = sorted([k for k in self._updates.keys() if k]) + raise RuntimeError( + f"Update handler for '{job.name}' expected but not found, and there is no dynamic handler. " + f"known updates: [{' '.join(known_updates)}]" + ) + args = self._process_handler_args( + job.name, + job.input, + defn.name, + defn.arg_types, + defn.dynamic_vararg, + ) + handler_input = HandleUpdateInput( + id=job.id, + update=job.name, + args=args, + headers=job.headers, + ) + + if job.run_validator and defn.validator is not None: + with self._as_read_only(): + self._inbound.handle_update_validator(handler_input) + # Re-process arguments to avoid any problems caused by user mutation of them during validation + args = self._process_handler_args( + job.name, + job.input, + defn.name, + defn.arg_types, + defn.dynamic_vararg, + ) + handler_input.args = args + + past_validation = True + # Accept the update + command.update_response.accepted.SetInParent() + command = None # type: ignore + + # Run the handler + success = await self._inbound.handle_update_handler(handler_input) + result_payloads = self._payload_converter.to_payloads([success]) + if len(result_payloads) != 1: + raise ValueError( + f"Expected 1 result payload, got {len(result_payloads)}" + ) + command = self._add_command() + command.update_response.protocol_instance_id = job.protocol_instance_id + command.update_response.completed.CopyFrom(result_payloads[0]) + except (Exception, asyncio.CancelledError) as err: + logger.debug( + f"Update raised failure with run ID {self._info.run_id}", + exc_info=True, + ) + # All asyncio cancelled errors become Temporal cancelled errors + if isinstance(err, asyncio.CancelledError): + err = temporalio.exceptions.CancelledError( + f"Cancellation raised within update {err}" + ) + # Read-only issues during validation should fail the task + if isinstance(err, temporalio.workflow.ReadOnlyContextError): + self._current_activation_error = err + return + # Temporal errors always fail the update. Other errors fail it during validation, but the task during + # handling. + if ( + isinstance(err, temporalio.exceptions.FailureError) + or not past_validation + ): + if command is None: + command = self._add_command() + command.update_response.protocol_instance_id = ( + job.protocol_instance_id + ) + command.update_response.rejected.SetInParent() + self._failure_converter.to_failure( + err, + self._payload_converter, + command.update_response.rejected, + ) + else: + self._current_activation_error = err + return + except BaseException as err: + # During tear down, generator exit and no-runtime exceptions can appear + if not self._deleting: + raise + if not isinstance( + err, + ( + GeneratorExit, + temporalio.workflow._NotInWorkflowEventLoopError, + ), + ): + logger.debug( + "Ignoring exception while deleting workflow", exc_info=True + ) + + self.create_task( + run_update(), + name=f"update: {job.name}", + ) + def _apply_fire_timer( self, job: temporalio.bridge.proto.workflow_activation.FireTimer ) -> None: @@ -767,6 +886,20 @@ def workflow_get_signal_handler(self, name: Optional[str]) -> Optional[Callable] # Bind if a method return defn.bind_fn(self._object) if defn.is_method else defn.fn + def workflow_get_update_handler(self, name: Optional[str]) -> Optional[Callable]: + defn = self._updates.get(name) + if not defn: + return None + # Bind if a method + return defn.bind_fn(self._object) if defn.is_method else defn.fn + + def workflow_get_update_validator(self, name: Optional[str]) -> Optional[Callable]: + defn = self._updates.get(name) or self._updates.get(None) + if not defn or not defn.validator: + return None + # Bind if a method + return defn.bind_validator(self._object) if defn.is_method else defn.validator + def workflow_info(self) -> temporalio.workflow.Info: return self._outbound.info() @@ -886,6 +1019,27 @@ def workflow_set_signal_handler( else: self._signals.pop(name, None) + def workflow_set_update_handler( + self, + name: Optional[str], + handler: Optional[Callable], + validator: Optional[Callable], + ) -> None: + self._assert_not_read_only("set update handler") + if handler: + defn = temporalio.workflow._UpdateDefinition( + name=name, fn=handler, is_method=False + ) + if validator is not None: + defn.set_validator(validator) + self._updates[name] = defn + if defn.dynamic_vararg: + raise RuntimeError( + "Dynamic updates do not support a vararg third param, use Sequence[RawValue]", + ) + else: + self._updates.pop(name, None) + def workflow_start_activity( self, activity: Any, @@ -1654,6 +1808,26 @@ async def handle_query(self, input: HandleQueryInput) -> Any: else: return handler(*input.args) + def handle_update_validator(self, input: HandleUpdateInput) -> None: + # Do not "or None" the validator, since we only want to use the validator for + # the specific named update - we shouldn't fall back to the dynamic validator + # for some defined, named update which doesn't have a defined validator. + handler = self._instance.workflow_get_update_validator(input.update) + # Validator may not be defined + if handler is not None: + handler(*input.args) + + async def handle_update_handler(self, input: HandleUpdateInput) -> Any: + handler = self._instance.workflow_get_update_handler( + input.update + ) or self._instance.workflow_get_update_handler(None) + # Handler should always be present at this point + assert handler + if inspect.iscoroutinefunction(handler): + return await handler(*input.args) + else: + return handler(*input.args) + class _WorkflowOutboundImpl(WorkflowOutboundInterceptor): def __init__(self, instance: _WorkflowInstanceImpl) -> None: diff --git a/temporalio/workflow.py b/temporalio/workflow.py index 2d5a7646..e28e47c0 100644 --- a/temporalio/workflow.py +++ b/temporalio/workflow.py @@ -36,7 +36,13 @@ overload, ) -from typing_extensions import Concatenate, Literal, TypedDict +from typing_extensions import ( + Concatenate, + Literal, + Protocol, + TypedDict, + runtime_checkable, +) import temporalio.api.common.v1 import temporalio.bridge.proto.child_workflow @@ -52,6 +58,7 @@ CallableAsyncType, CallableSyncNoParam, CallableSyncOrAsyncReturnNoneType, + CallableSyncOrAsyncType, CallableSyncSingleParam, CallableType, ClassType, @@ -63,6 +70,7 @@ MethodSyncSingleParam, MultiParamSpec, ParamType, + ProtocolReturnType, ReturnType, SelfType, ) @@ -446,6 +454,14 @@ def workflow_get_query_handler(self, name: Optional[str]) -> Optional[Callable]: def workflow_get_signal_handler(self, name: Optional[str]) -> Optional[Callable]: ... + @abstractmethod + def workflow_get_update_handler(self, name: Optional[str]) -> Optional[Callable]: + ... + + @abstractmethod + def workflow_get_update_validator(self, name: Optional[str]) -> Optional[Callable]: + ... + @abstractmethod def workflow_info(self) -> Info: ... @@ -496,6 +512,15 @@ def workflow_set_signal_handler( ) -> None: ... + @abstractmethod + def workflow_set_update_handler( + self, + name: Optional[str], + handler: Optional[Callable], + validator: Optional[Callable], + ) -> None: + ... + @abstractmethod def workflow_start_activity( self, @@ -746,6 +771,124 @@ def time_ns() -> int: return _Runtime.current().workflow_time_ns() +# Needs to be defined here to avoid a circular import +@runtime_checkable +class UpdateMethodMultiParam(Protocol[MultiParamSpec, ProtocolReturnType]): + """Decorated workflow update functions implement this.""" + + _defn: temporalio.workflow._UpdateDefinition + + def __call__( + self, *args: MultiParamSpec.args, **kwargs: MultiParamSpec.kwargs + ) -> Union[ProtocolReturnType, Awaitable[ProtocolReturnType]]: + """Generic callable type callback.""" + ... + + def validator( + self, vfunc: Callable[MultiParamSpec, None] + ) -> Callable[MultiParamSpec, None]: + """Use to decorate a function to validate the arguments passed to the update handler.""" + ... + + +@overload +def update( + fn: Callable[MultiParamSpec, Awaitable[ReturnType]] +) -> UpdateMethodMultiParam[MultiParamSpec, ReturnType]: + ... + + +@overload +def update( + fn: Callable[MultiParamSpec, ReturnType] +) -> UpdateMethodMultiParam[MultiParamSpec, ReturnType]: + ... + + +@overload +def update( + *, name: str +) -> Callable[ + [Callable[MultiParamSpec, ReturnType]], + UpdateMethodMultiParam[MultiParamSpec, ReturnType], +]: + ... + + +@overload +def update( + *, dynamic: Literal[True] +) -> Callable[ + [Callable[MultiParamSpec, ReturnType]], + UpdateMethodMultiParam[MultiParamSpec, ReturnType], +]: + ... + + +def update( + fn: Optional[CallableSyncOrAsyncType] = None, + *, + name: Optional[str] = None, + dynamic: Optional[bool] = False, +): + """Decorator for a workflow update handler method. + + This is set on any async or non-async method that you wish to be called upon + receiving an update. If a function overrides one with this decorator, it too + must be decorated. + + You may also optionally define a validator method that will be called before + this handler you have applied this decorator to. You can specify the validator + with ``@update_handler_function_name.validator``. + + Update methods can only have positional parameters. Best practice for + non-dynamic update methods is to only take a single object/dataclass + argument that can accept more fields later if needed. The handler may return + a serializable value which will be sent back to the caller of the update. + + .. warning:: + This API is experimental + + Args: + fn: The function to decorate. + name: Update name. Defaults to method ``__name__``. Cannot be present + when ``dynamic`` is present. + dynamic: If true, this handles all updates not otherwise handled. The + parameters of the method must be self, a string name, and a + ``*args`` positional varargs. Cannot be present when ``name`` is + present. + """ + + def with_name( + name: Optional[str], fn: CallableSyncOrAsyncType + ) -> CallableSyncOrAsyncType: + defn = _UpdateDefinition(name=name, fn=fn, is_method=True) + if defn.dynamic_vararg: + raise RuntimeError( + "Dynamic updates do not support a vararg third param, use Sequence[RawValue]", + ) + setattr(fn, "_defn", defn) + setattr(fn, "validator", partial(_update_validator, defn)) + return fn + + if name is not None or dynamic: + if name is not None and dynamic: + raise RuntimeError("Cannot provide name and dynamic boolean") + return partial(with_name, name) + if fn is None: + raise RuntimeError("Cannot create update without function or name or dynamic") + return with_name(fn.__name__, fn) + + +def _update_validator( + update_def: _UpdateDefinition, fn: Optional[Callable[..., None]] = None +) -> Optional[Callable[..., None]]: + """Decorator for a workflow update validator method.""" + if fn is not None: + update_def.set_validator(fn) + return fn + + def upsert_search_attributes(attributes: temporalio.common.SearchAttributes) -> None: """Upsert search attributes for this workflow. @@ -952,6 +1095,7 @@ class _Definition: run_fn: Callable[..., Awaitable] signals: Mapping[Optional[str], _SignalDefinition] queries: Mapping[Optional[str], _QueryDefinition] + updates: Mapping[Optional[str], _UpdateDefinition] sandboxed: bool # Types loaded on post init if both are None arg_types: Optional[List[Type]] = None @@ -998,12 +1142,13 @@ def _apply_to_class( raise ValueError("Class already contains workflow definition") issues: List[str] = [] - # Collect run fn and all signal/query fns + # Collect run fn and all signal/query/update fns members = inspect.getmembers(cls) run_fn: Optional[Callable[..., Awaitable[Any]]] = None seen_run_attr = False signals: Dict[Optional[str], _SignalDefinition] = {} queries: Dict[Optional[str], _QueryDefinition] = {} + updates: Dict[Optional[str], _UpdateDefinition] = {} for name, member in members: if hasattr(member, "__temporal_workflow_run"): seen_run_attr = True @@ -1045,6 +1190,16 @@ def _apply_to_class( ) else: queries[query_defn.name] = query_defn + elif isinstance(member, UpdateMethodMultiParam): + update_defn = member._defn + if update_defn.name in updates: + defn_name = update_defn.name or "" + issues.append( + f"Multiple update methods found for {defn_name} " + f"(at least on {name} and {updates[update_defn.name].fn.__name__})" + ) + else: + updates[update_defn.name] = update_defn # Check base classes haven't defined things with different decorators for base_cls in inspect.getmro(cls)[1:]: @@ -1078,6 +1233,12 @@ def _apply_to_class( issues.append( f"@workflow.query defined on {base_member.__qualname__} but not on the override" ) + elif isinstance(base_member, UpdateMethodMultiParam): + update_defn = base_member._defn + if update_defn.name not in updates: + issues.append( + f"@workflow.update defined on {base_member.__qualname__} but not on the override" + ) if not seen_run_attr: issues.append("Missing @workflow.run method") @@ -1095,6 +1256,7 @@ def _apply_to_class( run_fn=run_fn, signals=signals, queries=queries, + updates=updates, sandboxed=sandboxed, ) setattr(cls, "__temporal_workflow_definition", defn) @@ -1137,9 +1299,9 @@ async def with_object(*args, **kwargs) -> Any: def _assert_dynamic_handler_args( fn: Callable, arg_types: Optional[List[Type]], is_method: bool ) -> bool: - # Dynamic query/signal must have three args: self, name, and - # Sequence[RawValue]. An older form accepted varargs for the third param so - # we will too (but will warn in the signal/query code) + # Dynamic query/signal/update must have three args: self, name, and + # Sequence[RawValue]. An older form accepted varargs for the third param for signals/queries so + # we will too (but will warn in the signal/query code). params = list(inspect.signature(fn).parameters.values()) total_expected_params = 3 if is_method else 2 if ( @@ -1242,6 +1404,45 @@ def bind_fn(self, obj: Any) -> Callable[..., Any]: return _bind_method(obj, self.fn) +@dataclass(frozen=True) +class _UpdateDefinition: + # None if dynamic + name: Optional[str] + fn: Callable[..., Union[Any, Awaitable[Any]]] + is_method: bool + # Types loaded on post init if None + arg_types: Optional[List[Type]] = None + ret_type: Optional[Type] = None + validator: Optional[Callable[..., None]] = None + dynamic_vararg: bool = False + + def __post_init__(self) -> None: + if self.arg_types is None: + arg_types, ret_type = temporalio.common._type_hints_from_func(self.fn) + # Disallow dynamic varargs + if not self.name and not _assert_dynamic_handler_args( + self.fn, arg_types, self.is_method + ): + raise RuntimeError( + "Dynamic updates do not support a vararg third param, use Sequence[RawValue]", + ) + object.__setattr__(self, "arg_types", arg_types) + object.__setattr__(self, "ret_type", ret_type) + + def bind_fn(self, obj: Any) -> Callable[..., Any]: + return _bind_method(obj, self.fn) + + def bind_validator(self, obj: Any) -> Callable[..., Any]: + if self.validator is not None: + return _bind_method(obj, self.validator) + return lambda *args, **kwargs: None + + def set_validator(self, validator: Callable[..., None]) -> None: + if self.validator: + raise RuntimeError(f"Validator already set for update {self.name}") + object.__setattr__(self, "validator", validator) + + # See https://mypy.readthedocs.io/en/latest/runtime_troubles.html#using-classes-that-are-generic-in-stubs-but-not-at-runtime if TYPE_CHECKING: @@ -3948,6 +4149,64 @@ def set_dynamic_query_handler(handler: Optional[Callable]) -> None: _Runtime.current().workflow_set_query_handler(None, handler) +def get_update_handler(name: str) -> Optional[Callable]: + """Get the update handler for the given name if any. + + This includes handlers created via the ``@workflow.update`` decorator. + + Args: + name: Name of the update. + + Returns: + Callable for the update if any. If a handler is not found for the name, + this will not return the dynamic handler even if there is one. + """ + return _Runtime.current().workflow_get_update_handler(name) + + +def set_update_handler( + name: str, handler: Optional[Callable], *, validator: Optional[Callable] = None +) -> None: + """Set or unset the update handler for the given name. + + This overrides any existing handlers for the given name, including handlers + created via the ``@workflow.update`` decorator. + + Args: + name: Name of the update. + handler: Callable to set or None to unset. + validator: Callable to set or None to unset as the update validator. + """ + _Runtime.current().workflow_set_update_handler(name, handler, validator) + + +def get_dynamic_update_handler() -> Optional[Callable]: + """Get the dynamic update handler if any. + + This includes dynamic handlers created via the ``@workflow.update`` + decorator. + + Returns: + Callable for the dynamic update handler if any. + """ + return _Runtime.current().workflow_get_update_handler(None) + + +def set_dynamic_update_handler( + handler: Optional[Callable], *, validator: Optional[Callable] = None +) -> None: + """Set or unset the dynamic update handler. + + This overrides the existing dynamic handler even if it was created via the + ``@workflow.update`` decorator. + + Args: + handler: Callable to set or None to unset. + validator: Callable to set or None to unset as the update validator. + """ + _Runtime.current().workflow_set_update_handler(None, handler, validator) + + def _is_unbound_method_on_cls(fn: Callable[..., Any], cls: Type) -> bool: # Python 3 does not make this easy, ref https://stackoverflow.com/questions/3589311 return ( diff --git a/tests/contrib/test_opentelemetry.py b/tests/contrib/test_opentelemetry.py index 22bd8fbf..e9969aa8 100644 --- a/tests/contrib/test_opentelemetry.py +++ b/tests/contrib/test_opentelemetry.py @@ -19,7 +19,7 @@ from temporalio.contrib.opentelemetry import TracingInterceptor from temporalio.contrib.opentelemetry import workflow as otel_workflow from temporalio.testing import WorkflowEnvironment -from temporalio.worker import Worker +from temporalio.worker import UnsandboxedWorkflowRunner, Worker @dataclass @@ -48,6 +48,7 @@ class TracingWorkflowAction: activity: Optional[TracingWorkflowActionActivity] = None continue_as_new: Optional[TracingWorkflowActionContinueAsNew] = None wait_until_signal_count: int = 0 + wait_and_do_update: bool = False @dataclass @@ -71,10 +72,14 @@ class TracingWorkflowActionContinueAsNew: param: TracingWorkflowParam +ready_for_update: asyncio.Semaphore + + @workflow.defn class TracingWorkflow: def __init__(self) -> None: self._signal_count = 0 + self._did_update = False @workflow.run async def run(self, param: TracingWorkflowParam) -> None: @@ -126,6 +131,9 @@ async def run(self, param: TracingWorkflowParam) -> None: await workflow.wait_condition( lambda: self._signal_count >= action.wait_until_signal_count ) + if action.wait_and_do_update: + ready_for_update.release() + await workflow.wait_condition(lambda: self._did_update) async def _raise_on_non_replay(self) -> None: replaying = workflow.unsafe.is_replaying() @@ -143,6 +151,14 @@ def query(self) -> str: def signal(self) -> None: self._signal_count += 1 + @workflow.update + def update(self) -> None: + self._did_update = True + + @update.validator + def update_validator(self) -> None: + pass + async def test_opentelemetry_tracing(client: Client, env: WorkflowEnvironment): # TODO(cretz): Fix @@ -150,6 +166,8 @@ async def test_opentelemetry_tracing(client: Client, env: WorkflowEnvironment): pytest.skip( "Java test server: https://github.com/temporalio/sdk-java/issues/1424" ) + global ready_for_update + ready_for_update = asyncio.Semaphore(0) # Create a tracer that has an in-memory exporter exporter = InMemorySpanExporter() provider = TracerProvider() @@ -166,6 +184,8 @@ async def test_opentelemetry_tracing(client: Client, env: WorkflowEnvironment): task_queue=task_queue, workflows=[TracingWorkflow], activities=[tracing_activity], + # Needed so we can wait to send update at the right time + workflow_runner=UnsandboxedWorkflowRunner(), ): # Run workflow with various actions workflow_id = f"workflow_{uuid.uuid4()}" @@ -184,6 +204,8 @@ async def test_opentelemetry_tracing(client: Client, env: WorkflowEnvironment): fail_on_non_replay_before_complete=True, ), ), + # Wait for update + TracingWorkflowAction(wait_and_do_update=True), # Exec child workflow that fails task before complete TracingWorkflowAction( child_workflow=TracingWorkflowActionChildWorkflow( @@ -230,6 +252,10 @@ async def test_opentelemetry_tracing(client: Client, env: WorkflowEnvironment): # Send query, then signal to move it along assert "some query" == await handle.query(TracingWorkflow.query) await handle.signal(TracingWorkflow.signal) + # Wait to send the update until after the things that fail tasks are over, as failing a task while the update + # is running can mean we execute it twice, which will mess up our spans. + async with ready_for_update: + await handle.execute_update(TracingWorkflow.update) await handle.result() # Dump debug with attributes, but do string assertion test without @@ -245,6 +271,8 @@ async def test_opentelemetry_tracing(client: Client, env: WorkflowEnvironment): " StartActivity:tracing_activity", " RunActivity:tracing_activity", " RunActivity:tracing_activity", + " ValidateUpdate:update (links: StartWorkflowUpdate:update)", + " HandleUpdate:update (links: StartWorkflowUpdate:update)", " StartChildWorkflow:TracingWorkflow", " RunWorkflow:TracingWorkflow", " MyCustomSpan", @@ -263,6 +291,7 @@ async def test_opentelemetry_tracing(client: Client, env: WorkflowEnvironment): "QueryWorkflow:query", " HandleQuery:query (links: StartWorkflow:TracingWorkflow)", "SignalWorkflow:signal", + "StartWorkflowUpdate:update", ] diff --git a/tests/test_client.py b/tests/test_client.py index 5760f7a6..1772cc51 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -36,6 +36,7 @@ Client, Interceptor, OutboundInterceptor, + PollWorkflowUpdateInput, QueryWorkflowInput, RPCError, RPCStatusCode, @@ -55,6 +56,7 @@ ScheduleUpdateInput, SignalWorkflowInput, StartWorkflowInput, + StartWorkflowUpdateInput, TaskReachabilityType, TerminateWorkflowInput, WorkflowContinuedAsNewError, @@ -63,6 +65,7 @@ WorkflowHandle, WorkflowQueryFailedError, WorkflowQueryRejectedError, + WorkflowUpdateHandle, _history_from_json, ) from temporalio.common import RetryPolicy @@ -447,6 +450,18 @@ async def terminate_workflow(self, input: TerminateWorkflowInput) -> None: self._parent.traces.append(("terminate_workflow", input)) return await super().terminate_workflow(input) + async def start_workflow_update( + self, input: StartWorkflowUpdateInput + ) -> WorkflowUpdateHandle[Any]: + self._parent.traces.append(("start_workflow_update", input)) + return await super().start_workflow_update(input) + + async def poll_workflow_update( + self, input: PollWorkflowUpdateInput + ) -> WorkflowUpdateHandle[Any]: + self._parent.traces.append(("poll_workflow_update", input)) + return await super().poll_workflow_update(input) + async def test_interceptor(client: Client, worker: ExternalWorker): # Create new client from existing client but with a tracing interceptor diff --git a/tests/test_workflow.py b/tests/test_workflow.py index 3f24d530..f37a7374 100644 --- a/tests/test_workflow.py +++ b/tests/test_workflow.py @@ -19,6 +19,10 @@ def base_signal(self): def base_query(self): pass + @workflow.update + def base_update(self): + pass + @workflow.defn(name="workflow-custom") class GoodDefn(GoodDefnBase): @@ -50,6 +54,18 @@ def query2(self): def query3(self, name: str, args: Sequence[RawValue]): pass + @workflow.update + def update1(self): + pass + + @workflow.update(name="update-custom") + def update2(self): + pass + + @workflow.update(dynamic=True) + def update3(self, name: str, args: Sequence[RawValue]): + pass + def test_workflow_defn_good(): # Although the API is internal, we want to check the literal definition just @@ -87,6 +103,20 @@ def test_workflow_defn_good(): name="base_query", fn=GoodDefnBase.base_query, is_method=True ), }, + updates={ + "update1": workflow._UpdateDefinition( + name="update1", fn=GoodDefn.update1, is_method=True + ), + "update-custom": workflow._UpdateDefinition( + name="update-custom", fn=GoodDefn.update2, is_method=True + ), + None: workflow._UpdateDefinition( + name=None, fn=GoodDefn.update3, is_method=True + ), + "base_update": workflow._UpdateDefinition( + name="base_update", fn=GoodDefnBase.base_update, is_method=True + ), + }, sandboxed=True, ) @@ -100,6 +130,10 @@ def base_signal(self): def base_query(self): pass + @workflow.update + def base_update(self): + pass + class BadDefn(BadDefnBase): # Intentionally missing @workflow.run @@ -144,12 +178,24 @@ def query4(self, name: str, args: Sequence[RawValue]): def base_query(self): pass + @workflow.update + def update1(self, arg1: str): + pass + + @workflow.update(name="update1") + def update2(self, arg1: str): + pass + + # Intentionally missing decorator + def base_update(self): + pass + def test_workflow_defn_bad(): with pytest.raises(ValueError) as err: workflow.defn(BadDefn) - assert "Invalid workflow class for 7 reasons" in str(err.value) + assert "Invalid workflow class for 9 reasons" in str(err.value) assert "Missing @workflow.run method" in str(err.value) assert ( "Multiple signal methods found for signal1 (at least on signal2 and signal1)" @@ -175,6 +221,14 @@ def test_workflow_defn_bad(): "@workflow.query defined on BadDefnBase.base_query but not on the override" in str(err.value) ) + assert ( + "Multiple update methods found for update1 (at least on update2 and update1)" + in str(err.value) + ) + assert ( + "@workflow.update defined on BadDefnBase.base_update but not on the override" + in str(err.value) + ) def test_workflow_defn_local_class(): diff --git a/tests/worker/test_interceptor.py b/tests/worker/test_interceptor.py index 31ed3e9a..a9e726d5 100644 --- a/tests/worker/test_interceptor.py +++ b/tests/worker/test_interceptor.py @@ -6,7 +6,8 @@ import pytest from temporalio import activity, workflow -from temporalio.client import Client +from temporalio.client import Client, WorkflowUpdateFailedError +from temporalio.exceptions import ApplicationError from temporalio.testing import WorkflowEnvironment from temporalio.worker import ( ActivityInboundInterceptor, @@ -16,6 +17,7 @@ ExecuteWorkflowInput, HandleQueryInput, HandleSignalInput, + HandleUpdateInput, Interceptor, SignalChildWorkflowInput, SignalExternalWorkflowInput, @@ -78,6 +80,14 @@ async def handle_query(self, input: HandleQueryInput) -> Any: interceptor_traces.append(("workflow.query", input)) return await super().handle_query(input) + def handle_update_validator(self, input: HandleUpdateInput) -> None: + interceptor_traces.append(("workflow.update.validator", input)) + return super().handle_update_validator(input) + + async def handle_update_handler(self, input: HandleUpdateInput) -> Any: + interceptor_traces.append(("workflow.update.handler", input)) + return await super().handle_update_handler(input) + class TracingWorkflowOutboundInterceptor(WorkflowOutboundInterceptor): def continue_as_new(self, input: ContinueAsNewInput) -> NoReturn: @@ -167,6 +177,19 @@ def query(self, param: str) -> str: def signal(self, param: str) -> None: self.finish.set() + @workflow.update + def update(self, param: str) -> str: + return f"update: {param}" + + @workflow.update + def update_validated(self, param: str) -> str: + return f"update: {param}" + + @update_validated.validator + def update_validated_validator(self, param: str) -> None: + if param == "reject-me": + raise ApplicationError("Invalid update") + async def test_worker_interceptor(client: Client, env: WorkflowEnvironment): # TODO(cretz): Fix @@ -193,6 +216,13 @@ async def test_worker_interceptor(client: Client, env: WorkflowEnvironment): InterceptedWorkflow.query, "query-val" ) await handle.signal(InterceptedWorkflow.signal, "signal-val") + assert "update: update-val" == await handle.execute_update( + InterceptedWorkflow.update, "update-val" + ) + with pytest.raises(WorkflowUpdateFailedError) as _err: + await handle.execute_update( + InterceptedWorkflow.update_validated, "reject-me" + ) await handle.result() # Check traces @@ -246,5 +276,10 @@ def pop_trace(name: str, filter: Optional[Callable[[Any], bool]] = None) -> Any: assert pop_trace( "workflow.signal", lambda v: v.args[0] == "external-signal-val" ) + assert pop_trace("workflow.update.handler", lambda v: v.args[0] == "update-val") + assert pop_trace( + "workflow.update.validator", lambda v: v.args[0] == "reject-me" + ) + # Confirm no unexpected traces assert not interceptor_traces diff --git a/tests/worker/test_workflow.py b/tests/worker/test_workflow.py index 666863e2..6ead6f17 100644 --- a/tests/worker/test_workflow.py +++ b/tests/worker/test_workflow.py @@ -49,6 +49,7 @@ WorkflowFailureError, WorkflowHandle, WorkflowQueryFailedError, + WorkflowUpdateFailedError, ) from temporalio.common import RawValue, RetryPolicy, SearchAttributes from temporalio.converter import ( @@ -3503,3 +3504,242 @@ async def test_workflow_buffered_metrics(client: Client): and update.value == 1 for update in updates ) + + +bad_validator_fail_ct = 0 +task_fail_ct = 0 + + +@workflow.defn +class UpdateHandlersWorkflow: + def __init__(self) -> None: + self._last_event: Optional[str] = None + + @workflow.run + async def run(self) -> None: + workflow.set_update_handler("first_task_update", lambda: "worked") + + # Wait forever + await asyncio.Future() + + @workflow.update + def last_event(self, an_arg: str) -> str: + if an_arg == "fail": + raise ApplicationError("SyncFail") + le = self._last_event or "" + self._last_event = an_arg + return le + + @last_event.validator + def last_event_validator(self, an_arg: str) -> None: + workflow.logger.info("Running validator with arg %s", an_arg) + if an_arg == "reject_me": + raise ApplicationError("Rejected") + + @workflow.update + async def last_event_async(self, an_arg: str) -> str: + await asyncio.sleep(1) + if an_arg == "fail": + raise ApplicationError("AsyncFail") + le = self._last_event or "" + self._last_event = an_arg + return le + + @workflow.update + async def runs_activity(self, name: str) -> str: + act = workflow.start_activity( + say_hello, name, schedule_to_close_timeout=timedelta(seconds=5) + ) + act.cancel() + await act + return "done" + + @workflow.update(name="renamed") + async def async_named(self) -> str: + return "named" + + @workflow.update + async def bad_validator(self) -> str: + return "done" + + @bad_validator.validator + def bad_validator_validator(self) -> None: + global bad_validator_fail_ct + # Run a command which should not be allowed the first few tries, then "fix" it as if new code was deployed + if bad_validator_fail_ct < 2: + bad_validator_fail_ct += 1 + workflow.start_activity( + say_hello, "boo", schedule_to_close_timeout=timedelta(seconds=5) + ) + + @workflow.update + async def set_dynamic(self) -> str: + def dynahandler(name: str, _args: Sequence[RawValue]) -> str: + return "dynahandler - " + name + + def dynavalidator(name: str, _args: Sequence[RawValue]) -> None: + if name == "reject_me": + raise ApplicationError("Rejected") + + workflow.set_dynamic_update_handler(dynahandler, validator=dynavalidator) + return "set" + + @workflow.update + def throws_runtime_err(self) -> None: + global task_fail_ct + if task_fail_ct < 1: + task_fail_ct += 1 + raise RuntimeError("intentional failure") + + +async def test_workflow_update_handlers_happy(client: Client, env: WorkflowEnvironment): + if env.supports_time_skipping: + pytest.skip( + "Java test server: https://github.com/temporalio/sdk-java/issues/1903" + ) + async with new_worker( + client, UpdateHandlersWorkflow, activities=[say_hello] + ) as worker: + wf_id = f"update-handlers-workflow-{uuid.uuid4()}" + handle = await client.start_workflow( + UpdateHandlersWorkflow.run, + id=wf_id, + task_queue=worker.task_queue, + ) + + # Dynamically registered and used in first task + assert "worked" == await handle.execute_update("first_task_update") + + # Normal handling + last_event = await handle.execute_update( + UpdateHandlersWorkflow.last_event, "val2" + ) + assert "" == last_event + + # Async handler + last_event = await handle.execute_update( + UpdateHandlersWorkflow.last_event_async, "val3" + ) + assert "val2" == last_event + + # Dynamic handler + await handle.execute_update(UpdateHandlersWorkflow.set_dynamic) + assert "dynahandler - made_up" == await handle.execute_update("made_up") + + # Name overload + assert "named" == await handle.execute_update( + UpdateHandlersWorkflow.async_named + ) + + # Get untyped handle + assert "val3" == await client.get_workflow_handle(wf_id).execute_update( + UpdateHandlersWorkflow.last_event, "val4" + ) + + +async def test_workflow_update_handlers_unhappy( + client: Client, env: WorkflowEnvironment +): + if env.supports_time_skipping: + pytest.skip( + "Java test server: https://github.com/temporalio/sdk-java/issues/1903" + ) + async with new_worker(client, UpdateHandlersWorkflow) as worker: + handle = await client.start_workflow( + UpdateHandlersWorkflow.run, + id=f"update-handlers-workflow-unhappy-{uuid.uuid4()}", + task_queue=worker.task_queue, + ) + + # Undefined handler + with pytest.raises(WorkflowUpdateFailedError) as err: + await handle.execute_update("whargarbl", "whatever") + assert isinstance(err.value.cause, ApplicationError) + assert "'whargarbl' expected but not found" in err.value.cause.message + assert ( + "known updates: [bad_validator first_task_update last_event last_event_async renamed runs_activity set_dynamic throws_runtime_err]" + in err.value.cause.message + ) + + # Rejection by validator + with pytest.raises(WorkflowUpdateFailedError) as err: + await handle.execute_update(UpdateHandlersWorkflow.last_event, "reject_me") + assert isinstance(err.value.cause, ApplicationError) + assert "Rejected" == err.value.cause.message + + # Failure during update handler + with pytest.raises(WorkflowUpdateFailedError) as err: + await handle.execute_update(UpdateHandlersWorkflow.last_event, "fail") + assert isinstance(err.value.cause, ApplicationError) + assert "SyncFail" == err.value.cause.message + + with pytest.raises(WorkflowUpdateFailedError) as err: + await handle.execute_update(UpdateHandlersWorkflow.last_event_async, "fail") + assert isinstance(err.value.cause, ApplicationError) + assert "AsyncFail" == err.value.cause.message + + # Cancel inside handler + with pytest.raises(WorkflowUpdateFailedError) as err: + await handle.execute_update(UpdateHandlersWorkflow.runs_activity, "foo") + assert isinstance(err.value.cause, CancelledError) + + # Incorrect args for handler + with pytest.raises(WorkflowUpdateFailedError) as err: + await handle.execute_update("last_event", args=[121, "badarg"]) + assert isinstance(err.value.cause, ApplicationError) + assert ( + "last_event_validator() takes 2 positional arguments but 3 were given" + in err.value.cause.message + ) + + # Un-deserializeable nonsense + with pytest.raises(WorkflowUpdateFailedError) as err: + await handle.execute_update( + "last_event", + arg=RawValue( + payload=Payload( + metadata={"encoding": b"u-dont-know-me"}, data=b"enchi-cat" + ) + ), + ) + assert isinstance(err.value.cause, ApplicationError) + assert "Failed decoding arguments" == err.value.cause.message + + # Dynamic handler + await handle.execute_update(UpdateHandlersWorkflow.set_dynamic) + + # Rejection by dynamic handler validator + with pytest.raises(WorkflowUpdateFailedError) as err: + await handle.execute_update("reject_me") + assert isinstance(err.value.cause, ApplicationError) + assert "Rejected" == err.value.cause.message + + +async def test_workflow_update_task_fails(client: Client, env: WorkflowEnvironment): + if env.supports_time_skipping: + pytest.skip( + "Java test server: https://github.com/temporalio/sdk-java/issues/1903" + ) + # Need to not sandbox so behavior can change based on globals + async with new_worker( + client, UpdateHandlersWorkflow, workflow_runner=UnsandboxedWorkflowRunner() + ) as worker: + handle = await client.start_workflow( + UpdateHandlersWorkflow.run, + id=f"update-handlers-command-in-validator-{uuid.uuid4()}", + task_queue=worker.task_queue, + task_timeout=timedelta(seconds=1), + ) + + # This will produce a WFT failure which will eventually resolve and then this + # update will return + res = await handle.execute_update(UpdateHandlersWorkflow.bad_validator) + assert res == "done" + + # Non-temporal failure should cause task failure in update handler + await handle.execute_update(UpdateHandlersWorkflow.throws_runtime_err) + + # Verify task failures did happen + global task_fail_ct, bad_validator_fail_ct + assert task_fail_ct == 1 + assert bad_validator_fail_ct == 2