diff --git a/.vscode/settings.json b/.vscode/settings.json index ad92582bd0..e69de29bb2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +0,0 @@ -{ - "editor.formatOnSave": true -} diff --git a/docs/cloud/get-started/api-keys.mdx b/docs/cloud/get-started/api-keys.mdx index a86b25ff99..c4a0576864 100644 --- a/docs/cloud/get-started/api-keys.mdx +++ b/docs/cloud/get-started/api-keys.mdx @@ -411,7 +411,7 @@ To use your API key with a Temporal SDK, see the instructions in each SDK sectio [How to connect to Temporal Cloud using an API Key with the Java SDK](/develop/java/temporal-client#connect-to-temporal-cloud) -[How to connect to Temporal Cloud using an API Key with the Python SDK](/develop/python/temporal-client#connect-to-temporal-cloud) +[How to connect to Temporal Cloud using an API Key with the Python SDK](/develop/python/client/temporal-client#connect-to-temporal-cloud) [How to connect to Temporal Cloud using an API Key with the TypeScript SDK](/develop/typescript/temporal-client#connect-to-temporal-cloud) diff --git a/docs/cloud/get-started/certificates.mdx b/docs/cloud/get-started/certificates.mdx index dce0cb246a..4ef4cf0542 100644 --- a/docs/cloud/get-started/certificates.mdx +++ b/docs/cloud/get-started/certificates.mdx @@ -484,7 +484,7 @@ To view the current certificate filters, use the - [Go SDK](/develop/go/temporal-client#connect-to-temporal-cloud) - [Java SDK](/develop/java/temporal-client#connect-to-temporal-cloud) - [PHP SDK](/develop/php/temporal-client#connect-to-a-dev-cluster) -- [Python SDK](/develop/python/temporal-client#connect-to-temporal-cloud) +- [Python SDK](/develop/python/client/temporal-client#connect-to-temporal-cloud) - [TypeScript SDK](/develop/typescript/temporal-client#connect-to-temporal-cloud) - [.NET SDK](/develop/dotnet/temporal-client#connect-to-temporal-cloud) diff --git a/docs/cloud/get-started/index.mdx b/docs/cloud/get-started/index.mdx index 382983e2c3..7a194a1f8b 100644 --- a/docs/cloud/get-started/index.mdx +++ b/docs/cloud/get-started/index.mdx @@ -54,7 +54,7 @@ See our guides for connecting each SDK to your Temporal Cloud Namespace: - [Connect to Temporal Cloud in Go](/develop/go/temporal-client#connect-to-temporal-cloud) - [Connect to Temporal Cloud in Java](/develop/java/temporal-client#connect-to-temporal-cloud) -- [Connect to Temporal Cloud in Python](/develop/python/temporal-client#connect-to-temporal-cloud) +- [Connect to Temporal Cloud in Python](/develop/python/client/temporal-client#connect-to-temporal-cloud) - [Connect to Temporal Cloud in TypeScript](/develop/typescript/core-application#connect-to-temporal-cloud) - [Connect to Temporal Cloud in .NET](/develop/dotnet/temporal-client#connect-to-temporal-cloud) - [Connect to Temporal Cloud in PHP](/develop/php/temporal-client#connect-to-temporal-cloud) @@ -66,7 +66,7 @@ See our guides for starting a workflow using each SDK: - [Start a workflow in Go](/develop/go/temporal-client#start-workflow-execution) - [Start a workflow in Java](/develop/java/temporal-client#start-workflow-execution) -- [Start a workflow in Python](/develop/python/temporal-client#start-workflow-execution) +- [Start a workflow in Python](/develop/python/client/temporal-client#start-workflow-execution) - [Start a workflow in TypeScript](/develop/typescript/core-application#start-workflow-execution) - [Start a workflow in .NET](/develop/dotnet/temporal-client#start-workflow) - [Start a workflow in PHP](/develop/php/temporal-client#start-workflow-execution) diff --git a/docs/cloud/metrics/prometheus-grafana.mdx b/docs/cloud/metrics/prometheus-grafana.mdx index 71ffda59da..107c69005a 100644 --- a/docs/cloud/metrics/prometheus-grafana.mdx +++ b/docs/cloud/metrics/prometheus-grafana.mdx @@ -55,7 +55,7 @@ If you're following through with the examples provided here, ensure that you hav - [Go](/develop/go/temporal-client#connect-to-temporal-cloud) - [Java](/develop/java/temporal-client#connect-to-temporal-cloud) - [PHP](/develop/php/temporal-client#connect-to-a-dev-cluster) - - [Python](/develop/python/temporal-client#connect-to-temporal-cloud) + - [Python](/develop/python/client/temporal-client#connect-to-temporal-cloud) - [TypeScript](/develop/typescript/core-application#connect-to-temporal-cloud) - [.NET](/develop/dotnet/temporal-client#connect-to-temporal-cloud) @@ -95,7 +95,7 @@ Each language development guide has details on how to set this up. - [Go SDK](/develop/go/observability#metrics) - [Java SDK](/develop/java/observability#metrics) - [TypeScript SDK](/develop/typescript/observability#metrics) -- [Python](/develop/python/observability#metrics) +- [Python](/develop/python/workers/observability#metrics) - [.NET](/develop/dotnet/observability#metrics) The following example uses the Java SDK to set the Prometheus registry and Micrometer stats reporter, set the scope, and expose an endpoint from which Prometheus can scrape the SDK metrics. diff --git a/docs/develop/plugins-guide.mdx b/docs/develop/plugins-guide.mdx index 0f51224a81..abf15febb0 100644 --- a/docs/develop/plugins-guide.mdx +++ b/docs/develop/plugins-guide.mdx @@ -200,7 +200,7 @@ plugin = SimplePlugin( ### Interceptors -Interceptors are middleware that can run before and after various calls such as Activities, Workflows, and Signals. You can [learn more about interceptors](/develop/python/interceptors) for the details of implementing them. They're used to: +Interceptors are middleware that can run before and after various calls such as Activities, Workflows, and Signals. You can [learn more about interceptors](/develop/python/workers/interceptors) for the details of implementing them. They're used to: - Create side effects such as logging and tracing. - Modify arguments, such as adding headers for authorization or tracing propagation. @@ -222,7 +222,7 @@ plugin = SimplePlugin( Each of the SDKs has nuances you should be aware of so you can account for it in your code. -For example, you can choose to [run your Workflows in a sandbox in Python](/develop/python/python-sdk-sandbox). This lets you run Workflow code in a sandbox environment to help prevent non-determinism errors in your application. To work for users who use sandboxing, your Plugin should specify the Workflow runner that it uses. +For example, you can choose to [run your Workflows in a sandbox in Python](/develop/python/best-practices/python-sdk-sandbox). This lets you run Workflow code in a sandbox environment to help prevent non-determinism errors in your application. To work for users who use sandboxing, your Plugin should specify the Workflow runner that it uses. Here's an example of how to explicitly define the Workflow runner for your Plugin with Python: @@ -250,7 +250,7 @@ Two special concerns are versioning tests, for when you're making changes to you ### Versioning tests -When you make changes to your plugin after it has already shipped to users, it's recommended that you set up [replay testing](/develop/python/testing-suite#replay) on each important change to make sure that you’re not causing non-determinism errors for your users. +When you make changes to your plugin after it has already shipped to users, it's recommended that you set up [replay testing](/develop/python/best-practices/testing-suite#replay) on each important change to make sure that you’re not causing non-determinism errors for your users. ### Side effects tests diff --git a/docs/develop/python/asynchronous-activity-completion.mdx b/docs/develop/python/activities/asynchronous-activity-completion.mdx similarity index 100% rename from docs/develop/python/asynchronous-activity-completion.mdx rename to docs/develop/python/activities/asynchronous-activity-completion.mdx diff --git a/docs/develop/python/activities/basics.mdx b/docs/develop/python/activities/basics.mdx new file mode 100644 index 0000000000..3ffb36400b --- /dev/null +++ b/docs/develop/python/activities/basics.mdx @@ -0,0 +1,161 @@ +--- +id: basics +title: Activity Basics - Python SDK +sidebar_label: Activity Basics +description: This section explains Activity Basics with the Python SDK +toc_max_heading_level: 4 +keywords: + - Python SDK +tags: + - Python SDK + - Temporal SDKs +--- + +## Develop a basic Activity {#develop-activities} + +**How to develop a basic Activity using the Temporal Python SDK.** + +One of the primary things that Workflows do is orchestrate the execution of Activities. An Activity is a normal function +or method execution that's intended to execute a single, well-defined action (either short or long-running), such as +querying a database, calling a third-party API, or transcoding a media file. An Activity can interact with world outside +the Temporal Platform or use a Temporal Client to interact with a Temporal Service. For the Workflow to be able to +execute the Activity, we must define the [Activity Definition](/activity-definition). + +You can develop an Activity Definition by using the `@activity.defn` decorator. Register the function as an Activity +with a custom name through a decorator argument, for example `@activity.defn(name="your_activity")`. + +:::note + +The Temporal Python SDK supports multiple ways of implementing an Activity: + +- Asynchronously using [`asyncio`](https://docs.python.org/3/library/asyncio.html) +- Synchronously multithreaded using + [`concurrent.futures.ThreadPoolExecutor`](https://docs.python.org/3/library/concurrent.futures.html#threadpoolexecutor) +- Synchronously multiprocess using + [`concurrent.futures.ProcessPoolExecutor`](https://docs.python.org/3/library/concurrent.futures.html#processpoolexecutor) + and + [`multiprocessing.managers.SyncManager`](https://docs.python.org/3/library/multiprocessing.html#multiprocessing.managers.SyncManager) + +Blocking the async event loop in Python would turn your asynchronous program into a synchronous program that executes +serially, defeating the entire purpose of using `asyncio`. This can also lead to potential deadlock, and unpredictable +behavior that causes tasks to be unable to execute. Debugging these issues can be difficult and time consuming, as +locating the source of the blocking call might not always be immediately obvious. + +Due to this, consider not make blocking calls from within an asynchronous Activity, or use an async safe library to +perform these actions. If you must use a blocking library, consider using a synchronous Activity instead. + +::: + +
+ + View the source code + {' '} + in the context of the rest of the application code. +
+ +```python +from temporalio import activity +# ... +# ... +@activity.defn(name="your_activity") +async def your_activity(input: YourParams) -> str: + return f"{input.greeting}, {input.name}!" +``` + +### Develop Activity Parameters {#activity-parameters} + +**How to develop Activity Parameters using the Temporal Python SDK.** + +There is no explicit limit to the total number of parameters that an [Activity Definition](/activity-definition) may +support. However, there is a limit to the total size of the data that ends up encoded into a gRPC message Payload. + +A single argument is limited to a maximum size of 2 MB. And the total size of a gRPC message, which includes all the +arguments, is limited to a maximum of 4 MB. + +Also, keep in mind that all Payload data is recorded in the +[Workflow Execution Event History](/workflow-execution/event#event-history) and large Event Histories can affect Worker +performance. This is because the entire Event History could be transferred to a Worker Process with a +[Workflow Task](/tasks#workflow-task). + +{/* TODO link to gRPC limit section when available */} + +Some SDKs require that you pass context objects, others do not. When it comes to your application data—that is, data +that is serialized and encoded into a Payload—we recommend that you use a single object as an argument that wraps the +application data passed to Activities. This is so that you can change what data is passed to the Activity without +breaking a function or method signature. + +Activity parameters are the function parameters of the function decorated with `@activity.defn`. These can be any data +type Temporal can convert, including dataclasses when properly type-annotated. Technically this can be multiple +parameters, but Temporal strongly encourages a single dataclass parameter containing all input fields. + +
+ + View the source code + {' '} + in the context of the rest of the application code. +
+ +```python +from temporalio import activity +from your_dataobject_dacx import YourParams + +# ... +# ... +@activity.defn(name="your_activity") +async def your_activity(input: YourParams) -> str: + return f"{input.greeting}, {input.name}!" +``` + +### Define Activity return values {#activity-return-values} + +**How to define Activity return values using the Temporal Python SDK.** + +All data returned from an Activity must be serializable. + +Activity return values are subject to payload size limits in Temporal. The default payload size limit is 2MB, and there +is a hard limit of 4MB for any gRPC message size in the Event History transaction +([see Cloud limits here](https://docs.temporal.io/cloud/limits#per-message-grpc-limit)). Keep in mind that all return +values are recorded in a [Workflow Execution Event History](/workflow-execution/event#event-history). + +An Activity Execution can return inputs and other Activity values. + +The following example defines an Activity that takes an object as input and returns a string. + +
+ + View the source code + {' '} + in the context of the rest of the application code. +
+ +```python +# ... +@activity.defn(name="your_activity") +async def your_activity(input: YourParams) -> str: + return f"{input.greeting}, {input.name}!" +``` + +### Customize your Activity Type {#activity-type} + +**How to customize your Activity Type** + +Activities have a Type that are referred to as the Activity name. The following examples demonstrate how to set a custom +name for your Activity Type. + +You can customize the Activity name with a custom name in the decorator argument. For example, +`@activity.defn(name="your-activity")`. If the name parameter is not specified, the Activity name defaults to the +function name. + +
+ + View the source code + {' '} + in the context of the rest of the application code. +
+ +```python +# ... +@activity.defn(name="your_activity") +async def your_activity(input: YourParams) -> str: + return f"{input.greeting}, {input.name}!" +``` \ No newline at end of file diff --git a/docs/develop/python/benign-exceptions.mdx b/docs/develop/python/activities/benign-exceptions.mdx similarity index 100% rename from docs/develop/python/benign-exceptions.mdx rename to docs/develop/python/activities/benign-exceptions.mdx diff --git a/docs/develop/python/activities/execution.mdx b/docs/develop/python/activities/execution.mdx new file mode 100644 index 0000000000..8cc9b17f08 --- /dev/null +++ b/docs/develop/python/activities/execution.mdx @@ -0,0 +1,151 @@ +--- +id: execution +title: Activity execution - Python SDK +description: Shows how to perform Activity execution with the Python SDK +sidebar_label: Activity Execution +slug: /develop/python/activities/execution +toc_max_heading_level: 3 +tags: + - Python SDK + - Temporal SDKs + - Activity +--- + +## Start an Activity Execution {#activity-execution} + +**How to start an Activity Execution using the Temporal Python SDK.** + +Calls to spawn [Activity Executions](/activity-execution) are written within a +[Workflow Definition](/workflow-definition). The call to spawn an Activity Execution generates the +[ScheduleActivityTask](/references/commands#scheduleactivitytask) Command. This results in the set of three +[Activity Task](/tasks#activity-task) related Events ([ActivityTaskScheduled](/references/events#activitytaskscheduled), +[ActivityTaskStarted](/references/events#activitytaskstarted), and ActivityTask[Closed])in your Workflow Execution Event +History. + +A single instance of the Activities implementation is shared across multiple simultaneous Activity invocations. Activity +implementation code should be _idempotent_. + +The values passed to Activities through invocation parameters or returned through a result value are recorded in the +Execution history. The entire Execution history is transferred from the Temporal service to Workflow Workers when a +Workflow state needs to recover. A large Execution history can thus adversely impact the performance of your Workflow. + +Therefore, be mindful of the amount of data you transfer through Activity invocation parameters or Return Values. +Otherwise, no additional limitations exist on Activity implementations. + +To spawn an Activity Execution, use the +[`execute_activity()`](https://python.temporal.io/temporalio.workflow.html#execute_activity) operation from within your +Workflow Definition. + +`execute_activity()` is a shortcut for +[`start_activity()`](https://python.temporal.io/temporalio.workflow.html#start_activity) that waits on its result. + +To get just the handle to wait and cancel separately, use `start_activity()`. In most cases, use `execute_activity()` +unless advanced task capabilities are needed. + +A single argument to the Activity is positional. Multiple arguments are not supported in the type-safe form of +`start_activity()` or `execute_activity()` and must be supplied by the `args` keyword argument. + +
+ + View the source code + {' '} + in the context of the rest of the application code. +
+ +```python +from temporalio import workflow + +with workflow.unsafe.imports_passed_through(): + from your_activities_dacx import your_activity + from your_dataobject_dacx import YourParams +# ... +@workflow.defn(name="YourWorkflow") +class YourWorkflow: + @workflow.run + async def run(self, name: str) -> str: + return await workflow.execute_activity( + your_activity, + YourParams("Hello", name), + start_to_close_timeout=timedelta(seconds=10), + ) +``` + +### Set the required Activity Timeouts {#required-timeout} + +**How to set the required Activity Timeouts using the Temporal Python SDK.** + +Activity Execution semantics rely on several parameters. The only required value that needs to be set is either a +[Schedule-To-Close Timeout](/encyclopedia/detecting-activity-failures#schedule-to-close-timeout) or a +[Start-To-Close Timeout](/encyclopedia/detecting-activity-failures#start-to-close-timeout). These values are set in the +Activity Options. + +Activity options are set as keyword arguments after the Activity arguments. + +Available timeouts are: + +- schedule_to_close_timeout +- schedule_to_start_timeout +- start_to_close_timeout + +
+ + View the source code + {' '} + in the context of the rest of the application code. +
+ +```python +# ... + activity_timeout_result = await workflow.execute_activity( + your_activity, + YourParams(greeting, "Activity Timeout option"), + # Activity Execution Timeout + start_to_close_timeout=timedelta(seconds=10), + # schedule_to_start_timeout=timedelta(seconds=10), + # schedule_to_close_timeout=timedelta(seconds=10), + ) +``` + +### Get the results of an Activity Execution {#get-activity-results} + +**How to get the results of an Activity Execution using the Temporal Python SDK.** + +The call to spawn an [Activity Execution](/activity-execution) generates the +[ScheduleActivityTask](/references/commands#scheduleactivitytask) Command and provides the Workflow with an Awaitable. +Workflow Executions can either block progress until the result is available through the Awaitable or continue +progressing, making use of the result when it becomes available. + +Use [`start_activity()`](https://python.temporal.io/temporalio.workflow.html#start_activity) to start an Activity and +return its handle, [`ActivityHandle`](https://python.temporal.io/temporalio.workflow.ActivityHandle.html). Use +[`execute_activity()`](https://python.temporal.io/temporalio.workflow.html#execute_activity) to return the results. + +You must provide either `schedule_to_close_timeout` or `start_to_close_timeout`. + +`execute_activity()` is a shortcut for `await start_activity()`. An asynchronous `execute_activity()` helper is provided +which takes the same arguments as `start_activity()` and `await`s on the result. `execute_activity()` should be used in +most cases unless advanced task capabilities are needed. + +
+ + View the source code + {' '} + in the context of the rest of the application code. +
+ +```python +from temporalio import workflow + +with workflow.unsafe.imports_passed_through(): + from your_activities_dacx import your_activity + from your_dataobject_dacx import YourParams +# ... +@workflow.defn(name="YourWorkflow") +class YourWorkflow: + @workflow.run + async def run(self, name: str) -> str: + return await workflow.execute_activity( + your_activity, + YourParams("Hello", name), + start_to_close_timeout=timedelta(seconds=10), + ) +``` \ No newline at end of file diff --git a/docs/develop/python/activities/index.mdx b/docs/develop/python/activities/index.mdx new file mode 100644 index 0000000000..8ef2db62e0 --- /dev/null +++ b/docs/develop/python/activities/index.mdx @@ -0,0 +1,24 @@ +--- +id: index +title: Activities - Python SDK +sidebar_label: Activities +description: This section explains how to implement Activities with the Python SDK +toc_max_heading_level: 4 +keywords: + - Python SDK +tags: + - Python SDK + - Temporal SDKs +--- + +import * as Components from '@site/src/components'; + +![Python SDK Banner](/img/assets/banner-python-temporal.png) + +## Activities + +- [Activity Basics](/develop/python/activities/basics) +- [Activity Execution](/develop/python/activities/execution) +- [Timeouts](/develop/python/activities/timeouts) +- [Asynchronous Activity Completion](/develop/python/activities/asynchronous-activity-completion) +- [Benign exceptions](/develop/python/activities/benign-exceptions) \ No newline at end of file diff --git a/docs/develop/python/activities/timeouts.mdx b/docs/develop/python/activities/timeouts.mdx new file mode 100644 index 0000000000..a5c1b41720 --- /dev/null +++ b/docs/develop/python/activities/timeouts.mdx @@ -0,0 +1,174 @@ +--- +id: timeouts +title: Activity Timeouts - Python SDK +sidebar_label: Timeouts +description: Optimize Workflow Execution with Temporal Python SDK - Set Activity Timeouts and Retry Policies efficiently. +toc_max_heading_level: 4 +keywords: + - Python + - failure detection + - timeouts +tags: + - Activities + - Workflows + - Errors + - Failures + - Python SDK + - Temporal SDKs +--- + +## Set Activity timeouts {#activity-timeouts} + +Each Activity timeout controls the maximum duration of a different aspect of an Activity Execution. + +The following timeouts are available in the Activity Options. + +- **[Schedule-To-Close Timeout](/encyclopedia/detecting-activity-failures#schedule-to-close-timeout):** is the maximum amount of time allowed for the overall [Activity Execution](/activity-execution). +- **[Start-To-Close Timeout](/encyclopedia/detecting-activity-failures#start-to-close-timeout):** is the maximum time allowed for a single [Activity Task Execution](/tasks#activity-task-execution). +- **[Schedule-To-Start Timeout](/encyclopedia/detecting-activity-failures#schedule-to-start-timeout):** is the maximum amount of time that is allowed from when an [Activity Task](/tasks#activity-task) is scheduled to when a [Worker](/workers#worker) starts that Activity Task. + +An Activity Execution must have either the Start-To-Close or the Schedule-To-Close Timeout set. + +Activity options are set as keyword arguments after the Activity arguments. + +Available timeouts are: + +- schedule_to_close_timeout +- schedule_to_start_timeout +- start_to_close_timeout + +
+ + View the source code + {' '} + in the context of the rest of the application code. +
+ +```python +# ... + activity_timeout_result = await workflow.execute_activity( + your_activity, + YourParams(greeting, "Activity Timeout option"), + # Activity Execution Timeout + start_to_close_timeout=timedelta(seconds=10), + # schedule_to_start_timeout=timedelta(seconds=10), + # schedule_to_close_timeout=timedelta(seconds=10), + ) +``` + +### Set an Activity Retry Policy {#activity-retries} + +**How to set an Activity Retry Policy using the Temporal Python SDK** + +A Retry Policy works in cooperation with the timeouts to provide fine controls to optimize the execution experience. + +Activity Executions are automatically associated with a default [Retry Policy](/encyclopedia/retry-policies) if a custom one is not provided. + +To create an Activity Retry Policy in Python, set the [RetryPolicy](https://python.temporal.io/temporalio.common.RetryPolicy.html) class within the [`start_activity()`](https://python.temporal.io/temporalio.workflow.html#start_activity) or [`execute_activity()`](https://python.temporal.io/temporalio.workflow.html#execute_activity) function. + +
+ + View the source code + {' '} + in the context of the rest of the application code. +
+ +```python +from temporalio.common import RetryPolicy +# ... + activity_result = await workflow.execute_activity( + your_activity, + YourParams(greeting, "Retry Policy options"), + start_to_close_timeout=timedelta(seconds=10), + # Retry Policy + retry_policy=RetryPolicy( + backoff_coefficient=2.0, + maximum_attempts=5, + initial_interval=timedelta(seconds=1), + maximum_interval=timedelta(seconds=2), + # non_retryable_error_types=["ValueError"], + ), + ) +``` + +### Override the retry interval with `next_retry_delay` {#next-retry-delay} + +To override the next retry interval set by the current policy, pass `next_retry_delay` when raising an [ApplicationError](/references/failures#application-failure) in an Activity. +This value replaces and overrides whatever the retry interval would normally be on the retry policy. + +For example, you can set the delay interval based on an Activity's attempt count. +In the following example, the retry delay starts at 3 seconds after the first attempt. +It increases to 6 seconds for the second attempt, 9 seconds for the third attempt, and so forth. +This creates a steadily increasing backoff, versus the exponential approach used by [backoff coefficients](/encyclopedia/retry-policies#backoff-coefficient): + +```python +from temporalio.exceptions import ApplicationError +from datetime import timedelta + +@activity.defn +async def my_activity(input: MyActivityInput): + try: + # Your activity logic goes here + except Exception as e: + attempt = activity.info().attempt + raise ApplicationError( + f"Error encountered on attempt {attempt}", + next_retry_delay=timedelta(seconds=3 * attempt), + ) from e +``` + +## Heartbeat an Activity {#activity-heartbeats} + +**How to Heartbeat an Activity using the Temporal Python SDK** + +An [Activity Heartbeat](/encyclopedia/detecting-activity-failures#activity-heartbeat) is a ping from the [Worker Process](/workers#worker-process) that is executing the Activity to the [Temporal Service](/temporal-service). +Each Heartbeat informs the Temporal Service that the [Activity Execution](/activity-execution) is making progress and the Worker has not crashed. +If the Temporal Service does not receive a Heartbeat within a [Heartbeat Timeout](/encyclopedia/detecting-activity-failures#heartbeat-timeout) time period, the Activity will be considered failed and another [Activity Task Execution](/tasks#activity-task-execution) may be scheduled according to the Retry Policy. + +Heartbeats may not always be sent to the Temporal Service—they may be [throttled](/encyclopedia/detecting-activity-failures#throttling) by the Worker. + +Activity Cancellations are delivered to Activities from the Temporal Service when they Heartbeat. Activities that don't Heartbeat can't receive a Cancellation. +Heartbeat throttling may lead to Cancellation getting delivered later than expected. + +Heartbeats can contain a `details` field describing the Activity's current progress. +If an Activity gets retried, the Activity can access the `details` from the last Heartbeat that was sent to the Temporal Service. + +To Heartbeat an Activity Execution in Python, use the [`heartbeat()`](https://python.temporal.io/temporalio.activity.html#heartbeat) API. + +```python +@activity.defn +async def your_activity_definition() -> str: + activity.heartbeat("heartbeat details!") +``` + +In addition to obtaining cancellation information, Heartbeats also support detail data that persists on the server for retrieval during Activity retry. +If an Activity calls `heartbeat(123, 456)` and then fails and is retried, `heartbeat_details` returns an iterable containing `123` and `456` on the next Run. + +#### Set a Heartbeat Timeout {#heartbeat-timeout} + +**How to set a Heartbeat Timeout using the Temporal Python SDK** + +A [Heartbeat Timeout](/encyclopedia/detecting-activity-failures#heartbeat-timeout) works in conjunction with [Activity Heartbeats](/encyclopedia/detecting-activity-failures#activity-heartbeat). + +[`heartbeat_timeout`](https://python.temporal.io/temporalio.worker.StartActivityInput.html#heartbeat_timeout) is a class variable for the [`start_activity()`](https://python.temporal.io/temporalio.workflow.html#start_activity) function used to set the maximum time between Activity Heartbeats. + +```python +workflow.start_activity( + activity="your-activity", + schedule_to_close_timeout=timedelta(seconds=5), + heartbeat_timeout=timedelta(seconds=1), +) +``` + +`execute_activity()` is a shortcut for [`start_activity()`](https://python.temporal.io/temporalio.workflow.html#start_activity) that waits on its result. + +To get just the handle to wait and cancel separately, use `start_activity()`. `execute_activity()` should be used in most cases unless advanced task capabilities are needed. + +```python +workflow.execute_activity( + activity="your-activity", + name, + schedule_to_close_timeout=timedelta(seconds=5), + heartbeat_timeout=timedelta(seconds=1), +) +``` diff --git a/docs/develop/python/converters-and-encryption.mdx b/docs/develop/python/best-practices/converters-and-encryption.mdx similarity index 100% rename from docs/develop/python/converters-and-encryption.mdx rename to docs/develop/python/best-practices/converters-and-encryption.mdx diff --git a/docs/develop/python/debugging.mdx b/docs/develop/python/best-practices/debugging.mdx similarity index 87% rename from docs/develop/python/debugging.mdx rename to docs/develop/python/best-practices/debugging.mdx index 63c7cacae0..0278eb062f 100644 --- a/docs/develop/python/debugging.mdx +++ b/docs/develop/python/best-practices/debugging.mdx @@ -39,11 +39,11 @@ You can debug production Workflows using: - [Web UI](/web-ui) - [Temporal CLI](/cli) -- [Replay](/develop/python/testing-suite#replay) -- [Tracing](/develop/python/observability#tracing) -- [Logging](/develop/python/observability#logging) +- [Replay](/develop/python/best-practices/testing-suite#replay) +- [Tracing](/develop/python/workers/observability#tracing) +- [Logging](/develop/python/workers/observability#logging) You can debug and tune Worker performance with metrics and the [Worker performance guide](/develop/worker-performance). -For more information, see [Observability ▶️ Metrics](/develop/python/observability#metrics) for setting up SDK metrics. +For more information, see [Observability ▶️ Metrics](/develop/python/workers/observability#metrics) for setting up SDK metrics. Debug Server performance with [Cloud metrics](/cloud/metrics/) or [self-hosted Server metrics](/self-hosted-guide/production-checklist#scaling-and-metrics). diff --git a/docs/develop/python/error-handling.mdx b/docs/develop/python/best-practices/error-handling.mdx similarity index 94% rename from docs/develop/python/error-handling.mdx rename to docs/develop/python/best-practices/error-handling.mdx index 952258680a..530f3fb868 100644 --- a/docs/develop/python/error-handling.mdx +++ b/docs/develop/python/best-practices/error-handling.mdx @@ -3,7 +3,7 @@ id: error-handling title: Error handling - Python SDK sidebar_label: Error handling description: Learn how to handle errors in Temporal Python applications with retry policies, idempotent Activities, and recovery patterns. -slug: /develop/python/error-handling +slug: /develop/python/best-practices/error-handling toc_max_heading_level: 2 keywords: - error handling @@ -99,8 +99,13 @@ You might split this into three separate Activities so only the failed step retr **How to raise exceptions from Activities using the Temporal Python SDK** -Use `ApplicationError` to communicate application-specific failures from Activities. -Temporal converts any Python exception raised in an Activity to an `ApplicationError`, but raising it explicitly gives you more control. +Use `ApplicationError` to communicate application-specific failures from Activities. Raising it explicitly gives you more control. + +:::note + +*Any* other exceptions that are raised from your Python code in a Temporal Activity will be converted to an `ApplicationError` internally. This way, an error's type, severity, and any additional details can be sent to the Temporal Service, indexed by the Web UI, and even serialized across language boundaries. + +::: ```python from temporalio import activity @@ -132,7 +137,7 @@ When an Activity fails, Temporal wraps the exception in an `ActivityError` befor The `ActivityError` provides context including: - Activity type that failed - Number of retry attempts -- Original cause (the `ApplicationError` you raised, or `TimeoutError`, `CancelledError`, etc.) +- Original cause (the [`ApplicationError`](https://python.temporal.io/temporalio.exceptions.ApplicationError.html) you raised, or `TimeoutError`, [`CancelledError`](https://python.temporal.io/temporalio.exceptions.CancelledError.html), etc.) ## Raise exceptions from Workflows {#raise-exceptions-from-workflows} @@ -168,6 +173,10 @@ class PizzaDeliveryWorkflow: # Continue with order... ``` +One reason to use the Temporal `ApplicationError` class is because it allows you to set an additional `non_retryable` parameter. +This way, you can decide whether an error should not be retried automatically by Temporal. +This can be useful for deliberately failing a Workflow due to bad input data, rather than waiting for a timeout to elapse. You can alternately specify a list of errors that are non-retryable in your Activity [Retry Policy](/develop/python/activities/timeouts#activity-retries). + This puts the Workflow Execution in "Failed" state with no automatic retries. Use this for permanent failures where retrying won't help—like the customer being too far away. diff --git a/docs/develop/python/best-practices/index.mdx b/docs/develop/python/best-practices/index.mdx new file mode 100644 index 0000000000..5e52ff1010 --- /dev/null +++ b/docs/develop/python/best-practices/index.mdx @@ -0,0 +1,25 @@ +--- +id: index +title: Best Practices - Python SDK +sidebar_label: Best Practices +description: This section explains how to implement Best Practices with the Python SDK +toc_max_heading_level: 4 +keywords: + - Python SDK +tags: + - Python SDK + - Temporal SDKs +--- + +import * as Components from '@site/src/components'; + +![Python SDK Banner](/img/assets/banner-python-temporal.png) + +## Best practices + +- [Error handling](/develop/python/best-practices/error-handling) +- [Testing](/develop/python/best-practices/testing-suite) +- [Python SDK Sandbox](/develop/python/best-practices/python-sdk-sandbox) +- [Debugging](/develop/python/best-practices/debugging) +- [Converters and encryption](/develop/python/best-practices/converters-and-encryption) +- [Sync vs async](/develop/python/best-practices/python-sdk-sync-vs-async) \ No newline at end of file diff --git a/docs/develop/python/python-sdk-sandbox.mdx b/docs/develop/python/best-practices/python-sdk-sandbox.mdx similarity index 100% rename from docs/develop/python/python-sdk-sandbox.mdx rename to docs/develop/python/best-practices/python-sdk-sandbox.mdx diff --git a/docs/develop/python/python-sdk-sync-vs-async.mdx b/docs/develop/python/best-practices/python-sdk-sync-vs-async.mdx similarity index 100% rename from docs/develop/python/python-sdk-sync-vs-async.mdx rename to docs/develop/python/best-practices/python-sdk-sync-vs-async.mdx diff --git a/docs/develop/python/testing-suite.mdx b/docs/develop/python/best-practices/testing-suite.mdx similarity index 100% rename from docs/develop/python/testing-suite.mdx rename to docs/develop/python/best-practices/testing-suite.mdx diff --git a/docs/develop/python/client/index.mdx b/docs/develop/python/client/index.mdx new file mode 100644 index 0000000000..603ae46643 --- /dev/null +++ b/docs/develop/python/client/index.mdx @@ -0,0 +1,21 @@ +--- +id: index +title: Client - Python SDK +sidebar_label: Client +description: This section explains how to implement the Temporal Client with the Python SDK +toc_max_heading_level: 4 +keywords: + - Python SDK +tags: + - Python SDK + - Temporal SDKs +--- + +import * as Components from '@site/src/components'; + +![Python SDK Banner](/img/assets/banner-python-temporal.png) + +## Temporal Client + +- [Temporal Client](/develop/python/client/temporal-client) +- [Namespaces](/develop/python/client/namespaces) \ No newline at end of file diff --git a/docs/develop/python/client/namespaces.mdx b/docs/develop/python/client/namespaces.mdx new file mode 100644 index 0000000000..397eaffcbb --- /dev/null +++ b/docs/develop/python/client/namespaces.mdx @@ -0,0 +1,13 @@ +--- +id: namespaces +title: Namespaces - Python SDK +sidebar_label: Namespaces +toc_max_heading_level: 4 +keywords: + - namespaces +tags: + - Namespaces + - Python SDK + - Temporal SDKs +description: Register, update, deprecate, and delete Namespaces using Temporal CLI or SDK APIs. Manage Workflow Executions with isolated Namespaces to match your needs. +--- \ No newline at end of file diff --git a/docs/develop/python/temporal-client.mdx b/docs/develop/python/client/temporal-client.mdx similarity index 98% rename from docs/develop/python/temporal-client.mdx rename to docs/develop/python/client/temporal-client.mdx index 4268ae847f..f8d6dae26f 100644 --- a/docs/develop/python/temporal-client.mdx +++ b/docs/develop/python/client/temporal-client.mdx @@ -63,8 +63,8 @@ configure multiple profiles, each with its own set of connection options. You ca creating the Temporal Client. You can use the environment variable `TEMPORAL_CONFIG_FILE` to specify the location of the TOML file or provide the path to the file directly in code. If you don't provide the configuration file path, the SDK looks for it at the path `~/.config/temporalio/temporal.toml` or the equivalent on your OS. Refer to -[Environment Configuration](../environment-configuration.mdx#configuration-methods) for more details about configuration -files and profiles. +[Environment Configuration](/develop/environment-configuration#configuration-methods) for more details about +configuration files and profiles. :::info @@ -390,7 +390,7 @@ Ensure these environment variables exist in your environment before running your Import the `EnvConfig` package to set connection options for the Temporal Client using environment variables. The `MustLoadDefaultClientOptions` function will automatically load all environment variables. For a list of all available -environment variables and their default values, refer to [Environment Configuration](../environment-configuration). +environment variables and their default values, refer to [Environment Configuration](/develop/environment-configuration). For example, the following code snippet loads all environment variables and creates a Temporal Client with the options specified in those variables. If you have defined a configuration file at either the default location @@ -493,10 +493,10 @@ you must supply a Task Queue that will be used for the Tasks (one that a Worker language-specific contextual data, and Workflow Function parameters. In the examples below, all Workflow Executions are started using a Temporal Client. To spawn Workflow Executions from -within another Workflow Execution, use either the [Child Workflow](/develop/python/child-workflows) or External Workflow +within another Workflow Execution, use either the [Child Workflow](/develop/python/workflows/child-workflows) or External Workflow APIs. -See the [Customize Workflow Type](/develop/python/core-application#workflow-type) section to see how to customize the +See the [Customize Workflow Type](/develop/python/workflows/basics#workflow-type) section to see how to customize the name of the Workflow Type. A request to spawn a Workflow Execution causes the Temporal Service to create the first Event diff --git a/docs/develop/python/core-application.mdx b/docs/develop/python/core-application.mdx deleted file mode 100644 index 99d44b9713..0000000000 --- a/docs/develop/python/core-application.mdx +++ /dev/null @@ -1,590 +0,0 @@ ---- -id: core-application -title: Core application - Python SDK -sidebar_label: Core application -description: - Develop and customize Workflows and Activities using the Temporal Python SDK, manage parameters, set timeouts, execute - Activities, and run a Worker Process efficiently. -toc_max_heading_level: 2 -keywords: - - temporal python core - - develop temporal workflow - - temporal python activities - - workflow logic requirements - - customizing workflow type - - activity parameters - - activity return values - - activity execution in workflow - - activity timeouts - - getting activity results - - temporal python worker - - registering workflow types -tags: - - Activities - - Temporal Client - - Task Queues - - Workers - - Workflows - - Python SDK - - Temporal SDKs ---- - -This page shows how to do the following: - -- [Develop a basic Workflow](#develop-workflows) -- [Define Workflow parameters](#workflow-parameters) -- [Define Workflow return parameters](#workflow-return-values) -- [Customize your Workflow Type](#workflow-type) -- [Develop Workflow logic](#workflow-logic-requirements) -- [Develop a basic Activity](#develop-activities) -- [Develop Activity Parameters](#activity-parameters) -- [Define Activity return values](#activity-return-values) -- [Customize your Activity Type](#activity-type) -- [Start an Activity Execution](#activity-execution) -- [Set the required Activity Timeouts](#required-timeout) -- [Get the results of an Activity Execution](#get-activity-results) -- [Run a Worker Process](#run-a-dev-worker) -- [Register types](#register-types) - -## Develop a basic Workflow {#develop-workflows} - -**How to develop a basic Workflow using the Temporal Python SDK.** - -Workflows are the fundamental unit of a Temporal Application, and it all starts with the development of a -[Workflow Definition](/workflow-definition). - -In the Temporal Python SDK programming model, Workflows are defined as classes. - -Specify the `@workflow.defn` decorator on the Workflow class to identify a Workflow. - -Use the `@workflow.run` to mark the entry point method to be invoked. This must be set on one asynchronous method -defined on the same class as `@workflow.defn`. Run methods have positional parameters. - -
- - View the source code - {' '} - in the context of the rest of the application code. -
- -```python -from temporalio import workflow -# ... -# ... -@workflow.defn(name="YourWorkflow") -class YourWorkflow: - @workflow.run - async def run(self, name: str) -> str: - return await workflow.execute_activity( - your_activity, - YourParams("Hello", name), - start_to_close_timeout=timedelta(seconds=10), - ) -``` - -### Define Workflow parameters {#workflow-parameters} - -**How to define Workflow parameters using the Temporal Python SDK.** - -Temporal Workflows may have any number of custom parameters. However, we strongly recommend that objects are used as -parameters, so that the object's individual fields may be altered without breaking the signature of the Workflow. All -Workflow Definition parameters must be serializable. - -Workflow parameters are the method parameters of the singular method decorated with `@workflow.run`. These can be any -data type Temporal can convert, including [`dataclasses`](https://docs.python.org/3/library/dataclasses.html) when -properly type-annotated. Technically this can be multiple parameters, but Temporal strongly encourages a single -`dataclass` parameter containing all input fields. - -
- - View the source code - {' '} - in the context of the rest of the application code. -
- -```python -from dataclasses import dataclass -# ... -# ... -@dataclass -class YourParams: - greeting: str - name: str -``` - -### Define Workflow return parameters {#workflow-return-values} - -**How to define Workflow return parameters using the Temporal Python SDK.** - -Workflow return values must also be serializable. Returning results, returning errors, or throwing exceptions is fairly -idiomatic in each language that is supported. However, Temporal APIs that must be used to get the result of a Workflow -Execution will only ever receive one of either the result or the error. - -To return a value of the Workflow, use `return` to return an object. - -To return the results of a Workflow Execution, use either `start_workflow()` or `execute_workflow()` asynchronous -methods. - -For performance and behavior reasons, users should pass through all modules, including Activities, Nexus services, and -third-party plugins, whose calls will be deterministic using -[`imports_passed_through`](https://python.temporal.io/temporalio.workflow.unsafe.html#imports_passed_through) or at -Worker creation time by customizing the runner's restrictions with -[`with_passthrough_modules`](https://python.temporal.io/temporalio.worker.workflow_sandbox.SandboxRestrictions.html#with_passthrough_modules). - -
- - View the source code - {' '} - in the context of the rest of the application code. -
- -```python -from temporalio import workflow - -with workflow.unsafe.imports_passed_through(): - from your_activities_dacx import your_activity - from your_dataobject_dacx import YourParams -# ... -@workflow.defn(name="YourWorkflow") -class YourWorkflow: - @workflow.run - async def run(self, name: str) -> str: - return await workflow.execute_activity( - your_activity, - YourParams("Hello", name), - start_to_close_timeout=timedelta(seconds=10), - ) -``` - -### Customize your Workflow Type {#workflow-type} - -**How to customize your Workflow Type using the Temporal Python SDK.** - -Workflows have a Type that are referred to as the Workflow name. - -The following examples demonstrate how to set a custom name for your Workflow Type. - -You can customize the Workflow name with a custom name in the decorator argument. For example, -`@workflow.defn(name="your-workflow-name")`. If the name parameter is not specified, the Workflow name defaults to the -unqualified class name. - -
- - View the source code - {' '} - in the context of the rest of the application code. -
- -```python -from temporalio import workflow - -with workflow.unsafe.imports_passed_through(): - from your_activities_dacx import your_activity - from your_dataobject_dacx import YourParams -# ... -@workflow.defn(name="YourWorkflow") -class YourWorkflow: - @workflow.run - async def run(self, name: str) -> str: - return await workflow.execute_activity( - your_activity, - YourParams("Hello", name), - start_to_close_timeout=timedelta(seconds=10), - ) -``` - -### Develop Workflow logic {#workflow-logic-requirements} - -**How to develop Workflow logic using the Temporal Python SDK.** - -Workflow logic is constrained by [deterministic execution requirements](/workflow-definition#deterministic-constraints). -Therefore, each language is limited to the use of certain idiomatic techniques. However, each Temporal SDK provides a -set of APIs that can be used inside your Workflow to interact with external (to the Workflow) application code. - -Workflow code must be deterministic. This means: - -- no threading -- no randomness -- no external calls to processes -- no network I/O -- no global state mutation -- no system date or time - -All API safe for Workflows used in the [`temporalio.workflow`](https://python.temporal.io/temporalio.workflow.html) must -run in the implicit [`asyncio` event loop](https://docs.python.org/3/library/asyncio-eventloop.html) and be -_deterministic_. - -## Develop a basic Activity {#develop-activities} - -**How to develop a basic Activity using the Temporal Python SDK.** - -One of the primary things that Workflows do is orchestrate the execution of Activities. An Activity is a normal function -or method execution that's intended to execute a single, well-defined action (either short or long-running), such as -querying a database, calling a third-party API, or transcoding a media file. An Activity can interact with world outside -the Temporal Platform or use a Temporal Client to interact with a Temporal Service. For the Workflow to be able to -execute the Activity, we must define the [Activity Definition](/activity-definition). - -You can develop an Activity Definition by using the `@activity.defn` decorator. Register the function as an Activity -with a custom name through a decorator argument, for example `@activity.defn(name="your_activity")`. - -:::note - -The Temporal Python SDK supports multiple ways of implementing an Activity: - -- Asynchronously using [`asyncio`](https://docs.python.org/3/library/asyncio.html) -- Synchronously multithreaded using - [`concurrent.futures.ThreadPoolExecutor`](https://docs.python.org/3/library/concurrent.futures.html#threadpoolexecutor) -- Synchronously multiprocess using - [`concurrent.futures.ProcessPoolExecutor`](https://docs.python.org/3/library/concurrent.futures.html#processpoolexecutor) - and - [`multiprocessing.managers.SyncManager`](https://docs.python.org/3/library/multiprocessing.html#multiprocessing.managers.SyncManager) - -Blocking the async event loop in Python would turn your asynchronous program into a synchronous program that executes -serially, defeating the entire purpose of using `asyncio`. This can also lead to potential deadlock, and unpredictable -behavior that causes tasks to be unable to execute. Debugging these issues can be difficult and time consuming, as -locating the source of the blocking call might not always be immediately obvious. - -Due to this, consider not make blocking calls from within an asynchronous Activity, or use an async safe library to -perform these actions. If you must use a blocking library, consider using a synchronous Activity instead. - -::: - -
- - View the source code - {' '} - in the context of the rest of the application code. -
- -```python -from temporalio import activity -# ... -# ... -@activity.defn(name="your_activity") -async def your_activity(input: YourParams) -> str: - return f"{input.greeting}, {input.name}!" -``` - -### Develop Activity Parameters {#activity-parameters} - -**How to develop Activity Parameters using the Temporal Python SDK.** - -There is no explicit limit to the total number of parameters that an [Activity Definition](/activity-definition) may -support. However, there is a limit to the total size of the data that ends up encoded into a gRPC message Payload. - -A single argument is limited to a maximum size of 2 MB. And the total size of a gRPC message, which includes all the -arguments, is limited to a maximum of 4 MB. - -Also, keep in mind that all Payload data is recorded in the -[Workflow Execution Event History](/workflow-execution/event#event-history) and large Event Histories can affect Worker -performance. This is because the entire Event History could be transferred to a Worker Process with a -[Workflow Task](/tasks#workflow-task). - -{/* TODO link to gRPC limit section when available */} - -Some SDKs require that you pass context objects, others do not. When it comes to your application data—that is, data -that is serialized and encoded into a Payload—we recommend that you use a single object as an argument that wraps the -application data passed to Activities. This is so that you can change what data is passed to the Activity without -breaking a function or method signature. - -Activity parameters are the function parameters of the function decorated with `@activity.defn`. These can be any data -type Temporal can convert, including dataclasses when properly type-annotated. Technically this can be multiple -parameters, but Temporal strongly encourages a single dataclass parameter containing all input fields. - -
- - View the source code - {' '} - in the context of the rest of the application code. -
- -```python -from temporalio import activity -from your_dataobject_dacx import YourParams - -# ... -# ... -@activity.defn(name="your_activity") -async def your_activity(input: YourParams) -> str: - return f"{input.greeting}, {input.name}!" -``` - -### Define Activity return values {#activity-return-values} - -**How to define Activity return values using the Temporal Python SDK.** - -All data returned from an Activity must be serializable. - -Activity return values are subject to payload size limits in Temporal. The default payload size limit is 2MB, and there -is a hard limit of 4MB for any gRPC message size in the Event History transaction -([see Cloud limits here](https://docs.temporal.io/cloud/limits#per-message-grpc-limit)). Keep in mind that all return -values are recorded in a [Workflow Execution Event History](/workflow-execution/event#event-history). - -An Activity Execution can return inputs and other Activity values. - -The following example defines an Activity that takes an object as input and returns a string. - -
- - View the source code - {' '} - in the context of the rest of the application code. -
- -```python -# ... -@activity.defn(name="your_activity") -async def your_activity(input: YourParams) -> str: - return f"{input.greeting}, {input.name}!" -``` - -### Customize your Activity Type {#activity-type} - -**How to customize your Activity Type** - -Activities have a Type that are referred to as the Activity name. The following examples demonstrate how to set a custom -name for your Activity Type. - -You can customize the Activity name with a custom name in the decorator argument. For example, -`@activity.defn(name="your-activity")`. If the name parameter is not specified, the Activity name defaults to the -function name. - -
- - View the source code - {' '} - in the context of the rest of the application code. -
- -```python -# ... -@activity.defn(name="your_activity") -async def your_activity(input: YourParams) -> str: - return f"{input.greeting}, {input.name}!" -``` - -## Start an Activity Execution {#activity-execution} - -**How to start an Activity Execution using the Temporal Python SDK.** - -Calls to spawn [Activity Executions](/activity-execution) are written within a -[Workflow Definition](/workflow-definition). The call to spawn an Activity Execution generates the -[ScheduleActivityTask](/references/commands#scheduleactivitytask) Command. This results in the set of three -[Activity Task](/tasks#activity-task) related Events ([ActivityTaskScheduled](/references/events#activitytaskscheduled), -[ActivityTaskStarted](/references/events#activitytaskstarted), and ActivityTask[Closed])in your Workflow Execution Event -History. - -A single instance of the Activities implementation is shared across multiple simultaneous Activity invocations. Activity -implementation code should be _idempotent_. - -The values passed to Activities through invocation parameters or returned through a result value are recorded in the -Execution history. The entire Execution history is transferred from the Temporal service to Workflow Workers when a -Workflow state needs to recover. A large Execution history can thus adversely impact the performance of your Workflow. - -Therefore, be mindful of the amount of data you transfer through Activity invocation parameters or Return Values. -Otherwise, no additional limitations exist on Activity implementations. - -To spawn an Activity Execution, use the -[`execute_activity()`](https://python.temporal.io/temporalio.workflow.html#execute_activity) operation from within your -Workflow Definition. - -`execute_activity()` is a shortcut for -[`start_activity()`](https://python.temporal.io/temporalio.workflow.html#start_activity) that waits on its result. - -To get just the handle to wait and cancel separately, use `start_activity()`. In most cases, use `execute_activity()` -unless advanced task capabilities are needed. - -A single argument to the Activity is positional. Multiple arguments are not supported in the type-safe form of -`start_activity()` or `execute_activity()` and must be supplied by the `args` keyword argument. - -
- - View the source code - {' '} - in the context of the rest of the application code. -
- -```python -from temporalio import workflow - -with workflow.unsafe.imports_passed_through(): - from your_activities_dacx import your_activity - from your_dataobject_dacx import YourParams -# ... -@workflow.defn(name="YourWorkflow") -class YourWorkflow: - @workflow.run - async def run(self, name: str) -> str: - return await workflow.execute_activity( - your_activity, - YourParams("Hello", name), - start_to_close_timeout=timedelta(seconds=10), - ) -``` - -### Set the required Activity Timeouts {#required-timeout} - -**How to set the required Activity Timeouts using the Temporal Python SDK.** - -Activity Execution semantics rely on several parameters. The only required value that needs to be set is either a -[Schedule-To-Close Timeout](/encyclopedia/detecting-activity-failures#schedule-to-close-timeout) or a -[Start-To-Close Timeout](/encyclopedia/detecting-activity-failures#start-to-close-timeout). These values are set in the -Activity Options. - -Activity options are set as keyword arguments after the Activity arguments. - -Available timeouts are: - -- schedule_to_close_timeout -- schedule_to_start_timeout -- start_to_close_timeout - -
- - View the source code - {' '} - in the context of the rest of the application code. -
- -```python -# ... - activity_timeout_result = await workflow.execute_activity( - your_activity, - YourParams(greeting, "Activity Timeout option"), - # Activity Execution Timeout - start_to_close_timeout=timedelta(seconds=10), - # schedule_to_start_timeout=timedelta(seconds=10), - # schedule_to_close_timeout=timedelta(seconds=10), - ) -``` - -### Get the results of an Activity Execution {#get-activity-results} - -**How to get the results of an Activity Execution using the Temporal Python SDK.** - -The call to spawn an [Activity Execution](/activity-execution) generates the -[ScheduleActivityTask](/references/commands#scheduleactivitytask) Command and provides the Workflow with an Awaitable. -Workflow Executions can either block progress until the result is available through the Awaitable or continue -progressing, making use of the result when it becomes available. - -Use [`start_activity()`](https://python.temporal.io/temporalio.workflow.html#start_activity) to start an Activity and -return its handle, [`ActivityHandle`](https://python.temporal.io/temporalio.workflow.ActivityHandle.html). Use -[`execute_activity()`](https://python.temporal.io/temporalio.workflow.html#execute_activity) to return the results. - -You must provide either `schedule_to_close_timeout` or `start_to_close_timeout`. - -`execute_activity()` is a shortcut for `await start_activity()`. An asynchronous `execute_activity()` helper is provided -which takes the same arguments as `start_activity()` and `await`s on the result. `execute_activity()` should be used in -most cases unless advanced task capabilities are needed. - -
- - View the source code - {' '} - in the context of the rest of the application code. -
- -```python -from temporalio import workflow - -with workflow.unsafe.imports_passed_through(): - from your_activities_dacx import your_activity - from your_dataobject_dacx import YourParams -# ... -@workflow.defn(name="YourWorkflow") -class YourWorkflow: - @workflow.run - async def run(self, name: str) -> str: - return await workflow.execute_activity( - your_activity, - YourParams("Hello", name), - start_to_close_timeout=timedelta(seconds=10), - ) -``` - -## Run a Worker Process {#run-a-dev-worker} - -**How to run a Worker Process using the Temporal Python SDK.** - -The [Worker Process](/workers#worker-process) is where Workflow Functions and Activity Functions are executed. - -- Each [Worker Entity](/workers#worker-entity) in the Worker Process must register the exact Workflow Types and Activity - Types it may execute. -- Each Worker Entity must also associate itself with exactly one [Task Queue](/task-queue). -- Each Worker Entity polling the same Task Queue must be registered with the same Workflow Types and Activity Types. - -A [Worker Entity](/workers#worker-entity) is the component within a Worker Process that listens to a specific Task -Queue. - -Although multiple Worker Entities can be in a single Worker Process, a single Worker Entity Worker Process may be -perfectly sufficient. For more information, see the [Worker tuning guide](/develop/worker-performance). - -A Worker Entity contains a Workflow Worker and/or an Activity Worker, which makes progress on Workflow Executions and -Activity Executions, respectively. - -To develop a Worker, use the `Worker()` constructor and add your Client, Task Queue, Workflows, and Activities as -arguments. The following code example creates a Worker that polls for tasks from the Task Queue and executes the -Workflow. When a Worker is created, it accepts a list of Workflows in the workflows parameter, a list of Activities in -the activities parameter, or both. - -
- - View the source code - {' '} - in the context of the rest of the application code. -
- -```python -from temporalio.client import Client -from temporalio.worker import Worker -# ... -# ... -async def main(): - client = await Client.connect("localhost:7233") - worker = Worker( - client, - task_queue="your-task-queue", - workflows=[YourWorkflow], - activities=[your_activity], - ) - await worker.run() - -if __name__ == "__main__": - asyncio.run(main()) -``` - -### Register types {#register-types} - -**How to register types using the Temporal Python SDK.** - -All Workers listening to the same Task Queue name must be registered to handle the exact same Workflows Types and -Activity Types. - -If a Worker polls a Task for a Workflow Type or Activity Type it does not know about, it fails that Task. However, the -failure of the Task does not cause the associated Workflow Execution to fail. - -When a `Worker` is created, it accepts a list of Workflows in the `workflows` parameter, a list of Activities in the -`activities` parameter, or both. - -
- - View the source code - {' '} - in the context of the rest of the application code. -
- -```python -# ... -async def main(): - client = await Client.connect("localhost:7233") - worker = Worker( - client, - task_queue="your-task-queue", - workflows=[YourWorkflow], - activities=[your_activity], - ) - await worker.run() - -if __name__ == "__main__": - asyncio.run(main()) -``` diff --git a/docs/develop/python/failure-detection.mdx b/docs/develop/python/failure-detection.mdx deleted file mode 100644 index 5a3a4b2acb..0000000000 --- a/docs/develop/python/failure-detection.mdx +++ /dev/null @@ -1,360 +0,0 @@ ---- -id: failure-detection -title: Failure detection - Python SDK -sidebar_label: Failure detection -Description: Guidance on setting timeouts, retries, and heartbeat functionality for Workflows and Activities in Python with Temporal. -slug: /develop/python/failure-detection -toc_max_heading_level: 2 -keywords: - - workflow timeouts - - workflow retries - - activity timeouts - - activity retry policy - - activity heartbeats - - heartbeat timeout -tags: - - Activities - - Workflows - - Errors - - Failures - - Python SDK - - Temporal SDKs -description: Set Workflow and Activity timeouts, retries, retry policies, and heartbeats using the Temporal Python SDK to optimize execution and ensure reliability. ---- - -This page shows how to do the following: - -- [Raise and Handle Exceptions](#exception-handling) -- [Deliberately Fail Workflows](#workflow-failure) -- [Set Workflow Timeouts](#workflow-timeouts) -- [Set Workflow Retries](#workflow-retries) -- [Set Activity Timeouts](#activity-timeouts) -- [Set an Activity Retry Policy](#activity-retries) -- [Heartbeat an Activity](#activity-heartbeats) - -## Raise and Handle Exceptions {#exception-handling} - -In each Temporal SDK, error handling is implemented idiomatically, following the conventions of the language. -Temporal uses several different error classes internally — for example, [`CancelledError`](https://python.temporal.io/temporalio.exceptions.CancelledError.html) in the Python SDK, to handle a Workflow cancellation. -You should not raise or otherwise implement these manually, as they are tied to Temporal platform logic. - -The one Temporal error class that you will typically raise deliberately is [`ApplicationError`](https://python.temporal.io/temporalio.exceptions.ApplicationError.html). -In fact, *any* other exceptions that are raised from your Python code in a Temporal Activity will be converted to an `ApplicationError` internally. -This way, an error's type, severity, and any additional details can be sent to the Temporal Service, indexed by the Web UI, and even serialized across language boundaries. - -In other words, these two code samples do the same thing: - -```python -class MyCustomError(Exception): - def __init__(self, message, error_code): - super().__init__(message) - self.error_code = error_code - - def __str__(self): - return f"{self.message} (Error Code: {self.error_code})" - -@activity.defn -async def my_activity(input: MyActivityInput): - try: - # Your activity logic goes here - except Exception as e: - raise MyCustomError( - f"Error encountered on attempt {attempt}", - ) from e -``` - -```python -from temporalio.exceptions import ApplicationError - -@activity.defn -async def my_activity(input: MyActivityInput): - try: - # Your activity logic goes here - except Exception as e: - raise ApplicationError( - type="MyCustomError", - message=f"Error encountered on attempt {attempt}", - ) from e -``` - -Depending on your implementation, you may decide to use either method. -One reason to use the Temporal `ApplicationError` class is because it allows you to set an additional `non_retryable` parameter. -This way, you can decide whether an error should not be retried automatically by Temporal. -This can be useful for deliberately failing a Workflow due to bad input data, rather than waiting for a timeout to elapse: - -```python -from temporalio.exceptions import ApplicationError - -@activity.defn -async def my_activity(input: MyActivityInput): - try: - # Your activity logic goes here - except Exception as e: - raise ApplicationError( - type="MyNonRetryableError", - message=f"Error encountered on attempt {attempt}", - non_retryable=True, - ) from e -``` - -You can alternately specify a list of errors that are non-retryable in your Activity [Retry Policy](#activity-retries). - -## Failing Workflows {#workflow-failure} - -One of the core design principles of Temporal is that an Activity Failure will never directly cause a Workflow Failure — a Workflow should never return as Failed unless deliberately. -The default retry policy associated with Temporal Activities is to retry them until reaching a certain timeout threshold. -Activities will not actually *return* a failure to your Workflow until this condition or another non-retryable condition is met. -At this point, you can decide how to handle an error returned by your Activity the way you would in any other program. -For example, you could implement a [Saga Pattern](https://learn.temporal.io/tutorials/python/trip-booking-app/) that uses `try` and `except` blocks to "unwind" some of the steps your Workflow has performed up to the point of Activity Failure. - -**You will only fail a Workflow by manually raising an `ApplicationError` from the Workflow code.** -You could do this in response to an Activity Failure, if the failure of that Activity means that your Workflow should not continue: - -```python -try: - credit_card_confirmation = await workflow.execute_activity_method() -except ActivityError as e: - workflow.logger.error(f"Unable to process credit card {e.message}") - raise ApplicationError( - "Unable to process credit card", "CreditCardProcessingError" - ) -``` - -This works differently in a Workflow than raising exceptions from Activities. -In an Activity, any Python exceptions or custom exceptions are converted to a Temporal `ApplicationError`. -In a Workflow, any exceptions that are raised other than an explicit Temporal `ApplicationError` will only fail that particular [Workflow Task](https://docs.temporal.io/tasks#workflow-task-execution) and be retried. -This includes any typical Python runtime errors like a `NameError` or a `TypeError` that are raised automatically. -These errors are treated as bugs that can be corrected with a fixed deployment, rather than a reason for a Temporal Workflow Execution to return unexpectedly. - -## Workflow timeouts {#workflow-timeouts} - -**How to set Workflow timeouts using the Temporal Python SDK** - -Each Workflow timeout controls the maximum duration of a different aspect of a Workflow Execution. - -Before we continue, we want to note that we generally do not recommend setting Workflow Timeouts, because Workflows are designed to be long-running and resilient. -Instead, setting a Timeout can limit its ability to handle unexpected delays or long-running processes. -If you need to perform an action inside your Workflow after a specific period of time, we recommend using a Timer. - -Workflow timeouts are set when [starting the Workflow Execution](#workflow-timeouts). - -- **[Workflow Execution Timeout](/encyclopedia/detecting-workflow-failures#workflow-execution-timeout)** - restricts the maximum amount of time that a single Workflow Execution can be executed. -- **[Workflow Run Timeout](/encyclopedia/detecting-workflow-failures#workflow-run-timeout):** restricts the maximum amount of time that a single Workflow Run can last. -- **[Workflow Task Timeout](/encyclopedia/detecting-workflow-failures#workflow-task-timeout):** restricts the maximum amount of time that a Worker can execute a Workflow Task. - -Set the timeout to either the [`start_workflow()`](https://python.temporal.io/temporalio.client.Client.html#start_workflow) or [`execute_workflow()`](https://python.temporal.io/temporalio.client.Client.html#execute_workflow) asynchronous methods. - -Available timeouts are: - -- `execution_timeout` -- `run_timeout` -- `task_timeout` - -
- - View the source code - {' '} - in the context of the rest of the application code. -
- -```python -# ... - result = await client.execute_workflow( - YourWorkflow.run, - "your timeout argument", - id="your-workflow-id", - task_queue="your-task-queue", - # Set Workflow Timeout duration - execution_timeout=timedelta(seconds=2), - # run_timeout=timedelta(seconds=2), - # task_timeout=timedelta(seconds=2), - ) -``` - -### Workflow retries {#workflow-retries} - -**How to set a Workflow Retry Policy using the Temporal Python SDK** - -A Retry Policy can work in cooperation with the timeouts to provide fine controls to optimize the execution experience. - -Use a [Retry Policy](/encyclopedia/retry-policies) to retry a Workflow Execution in the event of a failure. - -Workflow Executions do not retry by default, and Retry Policies should be used with Workflow Executions only in certain situations. - -Set the Retry Policy to either the [`start_workflow()`](https://python.temporal.io/temporalio.client.Client.html#start_workflow) or [`execute_workflow()`](https://python.temporal.io/temporalio.client.Client.html#execute_workflow) asynchronous methods. - -
- - View the source code - {' '} - in the context of the rest of the application code. -
- -```python -# ... - handle = await client.execute_workflow( - YourWorkflow.run, - "your retry policy argument", - id="your-workflow-id", - task_queue="your-task-queue", - retry_policy=RetryPolicy(maximum_interval=timedelta(seconds=2)), - ) -``` - -## Set Activity timeouts {#activity-timeouts} - -**How to set an Activity Execution Timeout using the Temporal Python SDK** - -Each Activity timeout controls the maximum duration of a different aspect of an Activity Execution. - -The following timeouts are available in the Activity Options. - -- **[Schedule-To-Close Timeout](/encyclopedia/detecting-activity-failures#schedule-to-close-timeout):** is the maximum amount of time allowed for the overall [Activity Execution](/activity-execution). -- **[Start-To-Close Timeout](/encyclopedia/detecting-activity-failures#start-to-close-timeout):** is the maximum time allowed for a single [Activity Task Execution](/tasks#activity-task-execution). -- **[Schedule-To-Start Timeout](/encyclopedia/detecting-activity-failures#schedule-to-start-timeout):** is the maximum amount of time that is allowed from when an [Activity Task](/tasks#activity-task) is scheduled to when a [Worker](/workers#worker) starts that Activity Task. - -An Activity Execution must have either the Start-To-Close or the Schedule-To-Close Timeout set. - -Activity options are set as keyword arguments after the Activity arguments. - -Available timeouts are: - -- schedule_to_close_timeout -- schedule_to_start_timeout -- start_to_close_timeout - -
- - View the source code - {' '} - in the context of the rest of the application code. -
- -```python -# ... - activity_timeout_result = await workflow.execute_activity( - your_activity, - YourParams(greeting, "Activity Timeout option"), - # Activity Execution Timeout - start_to_close_timeout=timedelta(seconds=10), - # schedule_to_start_timeout=timedelta(seconds=10), - # schedule_to_close_timeout=timedelta(seconds=10), - ) -``` - -### Set an Activity Retry Policy {#activity-retries} - -**How to set an Activity Retry Policy using the Temporal Python SDK** - -A Retry Policy works in cooperation with the timeouts to provide fine controls to optimize the execution experience. - -Activity Executions are automatically associated with a default [Retry Policy](/encyclopedia/retry-policies) if a custom one is not provided. - -To create an Activity Retry Policy in Python, set the [RetryPolicy](https://python.temporal.io/temporalio.common.RetryPolicy.html) class within the [`start_activity()`](https://python.temporal.io/temporalio.workflow.html#start_activity) or [`execute_activity()`](https://python.temporal.io/temporalio.workflow.html#execute_activity) function. - -
- - View the source code - {' '} - in the context of the rest of the application code. -
- -```python -from temporalio.common import RetryPolicy -# ... - activity_result = await workflow.execute_activity( - your_activity, - YourParams(greeting, "Retry Policy options"), - start_to_close_timeout=timedelta(seconds=10), - # Retry Policy - retry_policy=RetryPolicy( - backoff_coefficient=2.0, - maximum_attempts=5, - initial_interval=timedelta(seconds=1), - maximum_interval=timedelta(seconds=2), - # non_retryable_error_types=["ValueError"], - ), - ) -``` - -### Override the retry interval with `next_retry_delay` {#next-retry-delay} - -To override the next retry interval set by the current policy, pass `next_retry_delay` when raising an [ApplicationError](/references/failures#application-failure) in an Activity. -This value replaces and overrides whatever the retry interval would normally be on the retry policy. - -For example, you can set the delay interval based on an Activity's attempt count. -In the following example, the retry delay starts at 3 seconds after the first attempt. -It increases to 6 seconds for the second attempt, 9 seconds for the third attempt, and so forth. -This creates a steadily increasing backoff, versus the exponential approach used by [backoff coefficients](/encyclopedia/retry-policies#backoff-coefficient): - -```python -from temporalio.exceptions import ApplicationError -from datetime import timedelta - -@activity.defn -async def my_activity(input: MyActivityInput): - try: - # Your activity logic goes here - except Exception as e: - attempt = activity.info().attempt - raise ApplicationError( - f"Error encountered on attempt {attempt}", - next_retry_delay=timedelta(seconds=3 * attempt), - ) from e -``` - -## Heartbeat an Activity {#activity-heartbeats} - -**How to Heartbeat an Activity using the Temporal Python SDK** - -An [Activity Heartbeat](/encyclopedia/detecting-activity-failures#activity-heartbeat) is a ping from the [Worker Process](/workers#worker-process) that is executing the Activity to the [Temporal Service](/temporal-service). -Each Heartbeat informs the Temporal Service that the [Activity Execution](/activity-execution) is making progress and the Worker has not crashed. -If the Temporal Service does not receive a Heartbeat within a [Heartbeat Timeout](/encyclopedia/detecting-activity-failures#heartbeat-timeout) time period, the Activity will be considered failed and another [Activity Task Execution](/tasks#activity-task-execution) may be scheduled according to the Retry Policy. - -Heartbeats may not always be sent to the Temporal Service—they may be [throttled](/encyclopedia/detecting-activity-failures#throttling) by the Worker. - -Activity Cancellations are delivered to Activities from the Temporal Service when they Heartbeat. Activities that don't Heartbeat can't receive a Cancellation. -Heartbeat throttling may lead to Cancellation getting delivered later than expected. - -Heartbeats can contain a `details` field describing the Activity's current progress. -If an Activity gets retried, the Activity can access the `details` from the last Heartbeat that was sent to the Temporal Service. - -To Heartbeat an Activity Execution in Python, use the [`heartbeat()`](https://python.temporal.io/temporalio.activity.html#heartbeat) API. - -```python -@activity.defn -async def your_activity_definition() -> str: - activity.heartbeat("heartbeat details!") -``` - -In addition to obtaining cancellation information, Heartbeats also support detail data that persists on the server for retrieval during Activity retry. -If an Activity calls `heartbeat(123, 456)` and then fails and is retried, `heartbeat_details` returns an iterable containing `123` and `456` on the next Run. - -#### Set a Heartbeat Timeout {#heartbeat-timeout} - -**How to set a Heartbeat Timeout using the Temporal Python SDK** - -A [Heartbeat Timeout](/encyclopedia/detecting-activity-failures#heartbeat-timeout) works in conjunction with [Activity Heartbeats](/encyclopedia/detecting-activity-failures#activity-heartbeat). - -[`heartbeat_timeout`](https://python.temporal.io/temporalio.worker.StartActivityInput.html#heartbeat_timeout) is a class variable for the [`start_activity()`](https://python.temporal.io/temporalio.workflow.html#start_activity) function used to set the maximum time between Activity Heartbeats. - -```python -workflow.start_activity( - activity="your-activity", - schedule_to_close_timeout=timedelta(seconds=5), - heartbeat_timeout=timedelta(seconds=1), -) -``` - -`execute_activity()` is a shortcut for [`start_activity()`](https://python.temporal.io/temporalio.workflow.html#start_activity) that waits on its result. - -To get just the handle to wait and cancel separately, use `start_activity()`. `execute_activity()` should be used in most cases unless advanced task capabilities are needed. - -```python -workflow.execute_activity( - activity="your-activity", - name, - schedule_to_close_timeout=timedelta(seconds=5), - heartbeat_timeout=timedelta(seconds=1), -) -``` diff --git a/docs/develop/python/index.mdx b/docs/develop/python/index.mdx index a5304efaa5..777dab03f5 100644 --- a/docs/develop/python/index.mdx +++ b/docs/develop/python/index.mdx @@ -2,7 +2,7 @@ id: index title: Python SDK developer guide sidebar_label: Python SDK -description: Explore Temporal's Python SDK feature guides to effortlessly develop, test, and manage Temporal Applications. Master Workflows, Activities, Workers, Failure Detection, and more! +description: Explore Temporal Python SDK feature guides to master developing Temporal Applications. Build Workflows, Activities, and Workers, connect to Temporal Services, set up a testing suite, handle failure detection, send messages, complete Activities asynchronously, implement Versioning, use Observability, debug applications, schedule Workflows toc_max_heading_level: 4 keywords: - Python SDK @@ -15,163 +15,77 @@ import * as Components from '@site/src/components'; ![Python SDK Banner](/img/assets/banner-python-temporal.png) -:::info PYTHON SPECIFIC RESOURCES -Build Temporal Applications with the Python SDK. +## Install and get started -**Temporal Python Technical Resources:** +You can find detailed installation instructions for the Python SDK in the [Quickstart](/develop/python/set-up-your-local-python). -- [Python SDK Quickstart - Setup Guide](https://docs.temporal.io/develop/python/set-up-your-local-python) -- [Python API Documentation](https://python.temporal.io) -- [Python SDK Code Samples](https://github.com/temporalio/samples-python) -- [Python SDK Github](https://github.com/temporalio/sdk-python) -- [Temporal 101 in Python Free Course](https://learn.temporal.io/courses/temporal_101/python/) - -**Get Connected with the Temporal Python Community:** - -- [Temporal Python Community Slack](https://app.slack.com/client/TNWA8QCGZ) -- [Python SDK Forum](https://community.temporal.io/tag/python-sdk) - ::: - -## [Core Application](/develop/python/core-application) - -Use the essential components of a Temporal Application (Workflows, Activities, and Workers) to build and run a Temporal application. - -- [Develop a Basic Workflow](/develop/python/core-application#develop-workflows) -- [Develop a Basic Activity](/develop/python/core-application#develop-activities) -- [Start an Activity Execution](/develop/python/core-application#activity-execution) -- [Run Worker Processes](/develop/python/core-application#run-a-dev-worker) - -## [Temporal Client](/develop/python/temporal-client) - -Connect to a Temporal Service and start a Workflow Execution. - -- [Connect to Development Temporal Service](/develop/python/temporal-client#connect-to-development-service) -- [Connect to Temporal Cloud](/develop/python/temporal-client#connect-to-temporal-cloud) -- [Start a Workflow Execution](/develop/python/temporal-client#start-workflow-execution) - -## [Python SDK Sandbox](/develop/python/python-sdk-sandbox) - -Use third-party Python modules without non-deterministic behavior. - -## [Python SDK sync vs. async implementations](/develop/python/python-sdk-sync-vs-async) - -Implement synchronous or asynchronous Activities. - -## [Testing](/develop/python/testing-suite) - -Set up the testing suite and test Workflows and Activities. - -- [Test Frameworks](/develop/python/testing-suite#test-frameworks) -- [Testing Activities](/develop/python/testing-suite#test-activities) -- [Testing Workflows](/develop/python/testing-suite#test-workflows) -- [How to Replay a Workflow Execution](/develop/python/testing-suite#replay) - -## [Failure detection](/develop/python/failure-detection) - -Explore how your application can detect failures using timeouts and automatically attempt to mitigate them with retries. - -- [Workflow Timeouts](/develop/python/failure-detection#workflow-timeouts) -- [Set Activity Timeouts](/develop/python/failure-detection#activity-timeouts) -- [Heartbeat an Activity](/develop/python/failure-detection#activity-heartbeats) - -## [Workflow message passing](/develop/python/message-passing) +There's also a short walkthrough of how to use the Temporal primitives (Activities, Workflows, and Workers) to build and run a Temporal application to get you up and running. -Send messages to and read the state of Workflow Executions. +Once your local Temporal Service is set up, continue building with the following resources: -- [Develop with Signals](/develop/python/message-passing#signals) -- [Develop with Queries](/develop/python/message-passing#queries) -- [Develop with Updates](/develop/python/message-passing#updates) -- [What is a Dynamic Handler](/develop/python/message-passing#dynamic-handler) +- [Activity Basics](/develop/python/activities/basics) +- [Workflow Basics](/develop/python/workflows/basics) +- [Start an Activity Execution](/develop/python/activities/execution) +- [Run Worker Processes](/develop/python/workers/run-worker-process) -## [Interrupt a Workflow feature guide](/develop/python/cancellation) +From there, you can dive deeper into any of the Temporal primitives to start building Workflows that fit your use cases. -Interrupt a Workflow Execution with a Cancel or Terminate action. +## [Workflows](/develop/python/workflows) -- [Cancel a Workflow](/develop/python/cancellation#cancellation) -- [Terminate a Workflow](/develop/python/cancellation#termination) -- [Reset a Workflow](/develop/python/cancellation#reset): Resume a Workflow Execution from an earlier point in its Event History. -- [Cancel an Activity from a Workflow](/develop/python/cancellation#cancel-activity) +- [Workflow Basics](/develop/python/workflows/basics) +- [Child Workflows](/develop/python/workflows/child-workflows) +- [Continue-As-New](/develop/python/workflows/continue-as-new) +- [Cancellation](/develop/python/workflows/cancellation) +- [Timeouts](/develop/python/workflows/timeouts) +- [Message Passing](/develop/python/workflows/message-passing) +- [Enriching the UI](/develop/python/workflows/enriching-ui) +- [Schedules](/develop/python/workflows/schedules) +- [Timers](/develop/python/workflows/timers) -## [Asynchronous Activity completion](/develop/python/asynchronous-activity-completion) +## [Activities](/develop/python/activities) -Complete Activities asynchronously. +- [Activity Basics](/develop/python/activities/basics) +- [Activity Execution](/develop/python/activities/execution) +- [Timeouts](/develop/python/activities/timeouts) +- [Asynchronous Activity Completion](/develop/python/activities/asynchronous-activity-completion) +- [Benign exceptions](/develop/python/activities/benign-exceptions) -- [Asynchronously Complete an Activity](/develop/python/asynchronous-activity-completion) +## [Workers](/develop/python/workers) -## [Versioning](/develop/python/versioning) +- [Run Worker processes](/develop/python/workers/run-worker-process) +- [Observability](/develop/python/workers/observability) -Change Workflow Definitions without causing non-deterministic behavior in running Workflows. +## [Temporal Client](/develop/python/client) -- [Introduction to Versioning](/develop/python/versioning) -- [How to Use the Patching API](/develop/python/versioning#patching) +- [Temporal Client](/develop/python/client/temporal-client) +- [Namespaces](/develop/python/client/namespaces) -## [Observability](/develop/python/observability) +## [Temporal Nexus](/develop/python/nexus) -Configure and use the Temporal Observability APIs. +- [Service Handlers](/develop/python/nexus/service-handler) -- [Emit Metrics](/develop/python/observability#metrics) -- [Set up tracing](/develop/python/observability#tracing) -- [Log from a Workflow](/develop/python/observability#logging) -- [Use Visibility APIs](/develop/python/observability#visibility) +## [Best practices](/develop/python/best-practices) -## [Debugging](/develop/python/debugging) +- [Testing](/develop/python/best-practices/testing-suite) +- [Python SDK Sandbox](/develop/python/best-practices/python-sdk-sandbox) +- [Debugging](/develop/python/best-practices/debugging) +- [Converters and encryption](/develop/python/best-practices/converters-and-encryption) +- [Sync vs async](/develop/python/best-practices/python-sdk-sync-vs-async) -Explore various ways to debug your application. +## [Integrations](/develop/python/integrations) -- [Debugging](/develop/python/debugging) +- [Braintrust integration](/develop/python/integrations/braintrust) -## [Schedules](/develop/python/schedules) -Run Workflows on a schedule and delay the start of a Workflow. +## Temporal Python Technical Resources -- [Schedule a Workflow](/develop/python/schedules#schedule-a-workflow) -- [Temporal Cron Jobs](/develop/python/schedules#temporal-cron-jobs) -- [Start Delay](/develop/python/schedules#start-delay) - -## [Data encryption](/develop/python/converters-and-encryption) - -Use compression, encryption, and other data handling by implementing custom converters and codecs. - -- [Custom Payload Codec](/develop/python/converters-and-encryption#custom-payload-codec) -- [Payload Conversion](/develop/python/converters-and-encryption#payload-conversion) - -## Temporal Nexus - -The [Temporal Nexus](/develop/python/nexus) feature guide shows how to use Temporal Nexus to connect durable executions within and across Namespaces using a Nexus Endpoint, a Nexus Service contract, and Nexus Operations. - -- [Create a Nexus Endpoint to route requests from caller to handler](/develop/python/nexus#create-nexus-endpoint) -- [Define the Nexus Service contract](/develop/python/nexus#define-nexus-service-contract) -- [Develop a Nexus Service and Operation handlers](/develop/python/nexus#develop-nexus-service-operation-handlers) -- [Develop a caller Workflow that uses a Nexus Service](/develop/python/nexus#develop-caller-workflow-nexus-service) -- [Make Nexus calls across Namespaces with a dev Server](/develop/python/nexus#register-the-caller-workflow-in-a-worker-and-start-the-caller-workflow) -- [Make Nexus calls across Namespaces in Temporal Cloud](/develop/python/nexus#nexus-calls-across-namespaces-temporal-cloud) - -## [Durable Timers](/develop/python/timers) - -Use Timers to make a Workflow Execution pause or "sleep" for seconds, minutes, days, months, or years. - -- [Sleep](/develop/python/timers) - -## [Child Workflows](/develop/python/child-workflows) - -Explore how to spawn a Child Workflow Execution and handle Child Workflow Events. - -- [Start a Child Workflow Execution](/develop/python/child-workflows) - -## [Continue-As-New](/develop/python/continue-as-new) - -Continue the Workflow Execution with a new Workflow Execution using the same Workflow ID. - -- [Continue-As-New](/develop/python/continue-as-new) - -## [Interceptors](/develop/python/interceptors) - -Manage inbound and outbound SDK calls, enhance tracing, and add authorization to your Workflows and Activities. - -- [Interceptors](/develop/python/interceptors) - -## [Enriching the User Interface](/develop/python/enriching-ui) +- [Python SDK Quickstart - Setup Guide](/develop/python/set-up-your-local-python) +- [Python API Documentation](https://python.temporal.io) +- [Python SDK Code Samples](https://github.com/temporalio/samples-python) +- [Python SDK Github](https://github.com/temporalio/sdk-python) +- [Temporal 101 in Python Free Course](https://learn.temporal.io/courses/temporal_101/python/) -Add descriptive information to workflows and events for better visibility and context in the UI. +## Get Connected with the Temporal Python Community -- [Adding Summary and Details to Workflows](/develop/python/enriching-ui#adding-summary-and-details-to-workflows) +- [Temporal Python Community Slack](https://app.slack.com/client/TNWA8QCGZ) +- [Python SDK Forum](https://community.temporal.io/tag/python-sdk) diff --git a/docs/develop/python/integrations/braintrust.mdx b/docs/develop/python/integrations/braintrust.mdx index 02c351fa1e..bba5e40b21 100644 --- a/docs/develop/python/integrations/braintrust.mdx +++ b/docs/develop/python/integrations/braintrust.mdx @@ -1,7 +1,7 @@ --- id: braintrust title: Braintrust integration -sidebar_label: Braintrust integration +sidebar_label: Braintrust toc_max_heading_level: 2 keywords: - ai @@ -47,7 +47,7 @@ complete code and run it locally. - If you are new to Temporal, we recommend reading [Understanding Temporal](/evaluate/understanding-temporal) or taking the [Temporal 101](https://learn.temporal.io/courses/temporal_101/) course. - Ensure you have set up your local development environment by following the - [Set up your local development environment](/develop/python/core-application) guide. When you're done, leave the + [Set up your local development environment](/develop/python/set-up-your-local-python) guide. When you're done, leave the Temporal Development Server running if you want to test your code locally. ## Configure Workers to use Braintrust diff --git a/docs/develop/python/integrations/index.mdx b/docs/develop/python/integrations/index.mdx index b85a493df3..3adade9aff 100644 --- a/docs/develop/python/integrations/index.mdx +++ b/docs/develop/python/integrations/index.mdx @@ -5,11 +5,21 @@ sidebar_label: AI integrations toc_max_heading_level: 2 keywords: - integrations + - ai frameworks + - agent frameworks tags: - Integrations + - AI Frameworks + - Agent Frameworks description: Integrations with other tools and services. --- -Temporal Python SDK provides integrations with the following tools and services: +The following AI framework integrations are available for the Temporal Python SDK: -- [Braintrust](./braintrust.mdx) +| Framework | SDK docs | Integration guide | +| --- | --- | --- | +| Braintrust | [braintrust.dev](https://braintrust.dev/docs) | [Guide](./braintrust.mdx) | +| Pydantic AI | [ai.pydantic.dev](https://ai.pydantic.dev/) | [Guide](https://ai.pydantic.dev/durable_execution/temporal/) | +| OpenAI Agents SDK | [openai.github.io](https://openai.github.io/openai-agents-python/) | [Guide](https://github.com/temporalio/sdk-python/blob/main/temporalio/contrib/openai_agents/README.md) | + +These integrations are built on the Temporal Python SDK's [Plugin system](/develop/plugins-guide), which you can also use to build your own integrations. \ No newline at end of file diff --git a/docs/develop/python/nexus/index.mdx b/docs/develop/python/nexus/index.mdx new file mode 100644 index 0000000000..09bf995ead --- /dev/null +++ b/docs/develop/python/nexus/index.mdx @@ -0,0 +1,20 @@ +--- +id: index +title: Nexus - Python SDK +sidebar_label: Nexus +description: This section explains how to use Temporal Nexus with the Python SDK +toc_max_heading_level: 4 +keywords: + - Python SDK +tags: + - Python SDK + - Temporal SDKs +--- + +import * as Components from '@site/src/components'; + +![Python SDK Banner](/img/assets/banner-python-temporal.png) + +## Temporal Nexus + +- [Service Handlers](/develop/python/nexus/service-handler) \ No newline at end of file diff --git a/docs/develop/python/temporal-nexus.mdx b/docs/develop/python/nexus/service-handler.mdx similarity index 98% rename from docs/develop/python/temporal-nexus.mdx rename to docs/develop/python/nexus/service-handler.mdx index 289c2b852f..45953ac69e 100644 --- a/docs/develop/python/temporal-nexus.mdx +++ b/docs/develop/python/nexus/service-handler.mdx @@ -1,13 +1,16 @@ --- -id: nexus -title: Temporal Nexus - Python SDK feature guide -sidebar_label: Temporal Nexus -description: Use Temporal Nexus within the Python SDK to connect Durable Executions within and across Namespaces using a Nexus Endpoint, a Nexus Service contract, and Nexus Operations. +id: service-handler +title: Nexus Service Handlers - Python SDK +sidebar_label: Service Handlers +slug: /develop/python/nexus/service-handler +description: + Use Temporal Nexus within the Python SDK to connect Durable Executions within and across Namespaces using a Nexus + Endpoint, a Nexus Service contract, and Nexus Operations. toc_max_heading_level: 4 keywords: - temporal - nexus - - python + - Python - sdk tags: - Nexus diff --git a/docs/develop/python/workers/basics.mdx b/docs/develop/python/workers/basics.mdx new file mode 100644 index 0000000000..edf045c23a --- /dev/null +++ b/docs/develop/python/workers/basics.mdx @@ -0,0 +1,12 @@ +--- +id: basics +title: Worker Basics - Python SDK +sidebar_label: Worker basics +description: This section explains Worker Basics with the Python SDK +toc_max_heading_level: 4 +keywords: + - Python SDK +tags: + - Python SDK + - Temporal SDKs +--- \ No newline at end of file diff --git a/docs/develop/python/workers/index.mdx b/docs/develop/python/workers/index.mdx new file mode 100644 index 0000000000..0cdbc5265c --- /dev/null +++ b/docs/develop/python/workers/index.mdx @@ -0,0 +1,21 @@ +--- +id: index +title: Workers - Python SDK +sidebar_label: Workers +description: This section explains how to implement Workers with the Python SDK +toc_max_heading_level: 4 +keywords: + - Python SDK +tags: + - Python SDK + - Temporal SDKs +--- + +import * as Components from '@site/src/components'; + +![Python SDK Banner](/img/assets/banner-python-temporal.png) + +## Workers + +- [Run Worker processes](/develop/python/workers/run-worker-process) +- [Observability](/develop/python/workers/observability) \ No newline at end of file diff --git a/docs/develop/python/interceptors.mdx b/docs/develop/python/workers/interceptors.mdx similarity index 100% rename from docs/develop/python/interceptors.mdx rename to docs/develop/python/workers/interceptors.mdx diff --git a/docs/develop/python/observability.mdx b/docs/develop/python/workers/observability.mdx similarity index 99% rename from docs/develop/python/observability.mdx rename to docs/develop/python/workers/observability.mdx index ff67b51ea2..1bc89a465d 100644 --- a/docs/develop/python/observability.mdx +++ b/docs/develop/python/workers/observability.mdx @@ -3,7 +3,7 @@ id: observability title: Observability - Python SDK sidebar_label: Observability description: Discover how to monitor your Temporal Application using metrics, tracing, logging, and visibility APIs. Emit metrics, set up tracing, log from Workflows, and use custom Search Attributes. -slug: /develop/python/observability +slug: /develop/python/workers/observability toc_max_heading_level: 2 keywords: - client diff --git a/docs/develop/python/workers/run-process.mdx b/docs/develop/python/workers/run-process.mdx new file mode 100644 index 0000000000..e308dd7eb0 --- /dev/null +++ b/docs/develop/python/workers/run-process.mdx @@ -0,0 +1,99 @@ +--- +id: run-worker-process +title: Run Worker processes - Python SDK +description: Shows how to run Worker processes with the Python SDK +sidebar_label: Worker processes +slug: /develop/python/workers/run-worker-process +toc_max_heading_level: 3 +tags: + - Python SDK + - Temporal SDKs + - Worker +--- + +## Run a Worker Process {#run-a-dev-worker} + +**How to run a Worker Process using the Temporal Python SDK.** + +The [Worker Process](/workers#worker-process) is where Workflow Functions and Activity Functions are executed. + +- Each [Worker Entity](/workers#worker-entity) in the Worker Process must register the exact Workflow Types and Activity + Types it may execute. +- Each Worker Entity must also associate itself with exactly one [Task Queue](/task-queue). +- Each Worker Entity polling the same Task Queue must be registered with the same Workflow Types and Activity Types. + +A [Worker Entity](/workers#worker-entity) is the component within a Worker Process that listens to a specific Task +Queue. + +Although multiple Worker Entities can be in a single Worker Process, a single Worker Entity Worker Process may be +perfectly sufficient. For more information, see the [Worker tuning guide](/develop/worker-performance). + +A Worker Entity contains a Workflow Worker and/or an Activity Worker, which makes progress on Workflow Executions and +Activity Executions, respectively. + +To develop a Worker, use the `Worker()` constructor and add your Client, Task Queue, Workflows, and Activities as +arguments. The following code example creates a Worker that polls for tasks from the Task Queue and executes the +Workflow. When a Worker is created, it accepts a list of Workflows in the workflows parameter, a list of Activities in +the activities parameter, or both. + +
+ + View the source code + {' '} + in the context of the rest of the application code. +
+ +```python +from temporalio.client import Client +from temporalio.worker import Worker +# ... +# ... +async def main(): + client = await Client.connect("localhost:7233") + worker = Worker( + client, + task_queue="your-task-queue", + workflows=[YourWorkflow], + activities=[your_activity], + ) + await worker.run() + +if __name__ == "__main__": + asyncio.run(main()) +``` + +### Register types {#register-types} + +**How to register types using the Temporal Python SDK.** + +All Workers listening to the same Task Queue name must be registered to handle the exact same Workflows Types and +Activity Types. + +If a Worker polls a Task for a Workflow Type or Activity Type it does not know about, it fails that Task. However, the +failure of the Task does not cause the associated Workflow Execution to fail. + +When a `Worker` is created, it accepts a list of Workflows in the `workflows` parameter, a list of Activities in the +`activities` parameter, or both. + +
+ + View the source code + {' '} + in the context of the rest of the application code. +
+ +```python +# ... +async def main(): + client = await Client.connect("localhost:7233") + worker = Worker( + client, + task_queue="your-task-queue", + workflows=[YourWorkflow], + activities=[your_activity], + ) + await worker.run() + +if __name__ == "__main__": + asyncio.run(main()) +``` diff --git a/docs/develop/python/workflows/basics.mdx b/docs/develop/python/workflows/basics.mdx new file mode 100644 index 0000000000..a5e3bc6a52 --- /dev/null +++ b/docs/develop/python/workflows/basics.mdx @@ -0,0 +1,180 @@ +--- +id: basics +title: Workflow Basics - Python SDK +sidebar_label: Workflow basics +description: This section explains Workflow Basics with the Python SDK +toc_max_heading_level: 4 +keywords: + - Python SDK +tags: + - Python SDK + - Temporal SDKs +--- + +## Develop a basic Workflow {#develop-workflows} + +**How to develop a basic Workflow using the Temporal Python SDK.** + +Workflows are the fundamental unit of a Temporal Application, and it all starts with the development of a +[Workflow Definition](/workflow-definition). + +In the Temporal Python SDK programming model, Workflows are defined as classes. + +Specify the `@workflow.defn` decorator on the Workflow class to identify a Workflow. + +Use the `@workflow.run` to mark the entry point method to be invoked. This must be set on one asynchronous method +defined on the same class as `@workflow.defn`. Run methods have positional parameters. + +
+ + View the source code + {' '} + in the context of the rest of the application code. +
+ +```python +from temporalio import workflow +# ... +# ... +@workflow.defn(name="YourWorkflow") +class YourWorkflow: + @workflow.run + async def run(self, name: str) -> str: + return await workflow.execute_activity( + your_activity, + YourParams("Hello", name), + start_to_close_timeout=timedelta(seconds=10), + ) +``` + +### Define Workflow parameters {#workflow-parameters} + +**How to define Workflow parameters using the Temporal Python SDK.** + +Temporal Workflows may have any number of custom parameters. However, we strongly recommend that objects are used as +parameters, so that the object's individual fields may be altered without breaking the signature of the Workflow. All +Workflow Definition parameters must be serializable. + +Workflow parameters are the method parameters of the singular method decorated with `@workflow.run`. These can be any +data type Temporal can convert, including [`dataclasses`](https://docs.python.org/3/library/dataclasses.html) when +properly type-annotated. Technically this can be multiple parameters, but Temporal strongly encourages a single +`dataclass` parameter containing all input fields. + +
+ + View the source code + {' '} + in the context of the rest of the application code. +
+ +```python +from dataclasses import dataclass +# ... +# ... +@dataclass +class YourParams: + greeting: str + name: str +``` + +### Define Workflow return parameters {#workflow-return-values} + +**How to define Workflow return parameters using the Temporal Python SDK.** + +Workflow return values must also be serializable. Returning results, returning errors, or throwing exceptions is fairly +idiomatic in each language that is supported. However, Temporal APIs that must be used to get the result of a Workflow +Execution will only ever receive one of either the result or the error. + +To return a value of the Workflow, use `return` to return an object. + +To return the results of a Workflow Execution, use either `start_workflow()` or `execute_workflow()` asynchronous +methods. + +For performance and behavior reasons, users should pass through all modules, including Activities, Nexus services, and +third-party plugins, whose calls will be deterministic using +[`imports_passed_through`](https://python.temporal.io/temporalio.workflow.unsafe.html#imports_passed_through) or at +Worker creation time by customizing the runner's restrictions with +[`with_passthrough_modules`](https://python.temporal.io/temporalio.worker.workflow_sandbox.SandboxRestrictions.html#with_passthrough_modules). + +
+ + View the source code + {' '} + in the context of the rest of the application code. +
+ +```python +from temporalio import workflow + +with workflow.unsafe.imports_passed_through(): + from your_activities_dacx import your_activity + from your_dataobject_dacx import YourParams +# ... +@workflow.defn(name="YourWorkflow") +class YourWorkflow: + @workflow.run + async def run(self, name: str) -> str: + return await workflow.execute_activity( + your_activity, + YourParams("Hello", name), + start_to_close_timeout=timedelta(seconds=10), + ) +``` + +### Customize your Workflow Type {#workflow-type} + +**How to customize your Workflow Type using the Temporal Python SDK.** + +Workflows have a Type that are referred to as the Workflow name. + +The following examples demonstrate how to set a custom name for your Workflow Type. + +You can customize the Workflow name with a custom name in the decorator argument. For example, +`@workflow.defn(name="your-workflow-name")`. If the name parameter is not specified, the Workflow name defaults to the +unqualified class name. + +
+ + View the source code + {' '} + in the context of the rest of the application code. +
+ +```python +from temporalio import workflow + +with workflow.unsafe.imports_passed_through(): + from your_activities_dacx import your_activity + from your_dataobject_dacx import YourParams +# ... +@workflow.defn(name="YourWorkflow") +class YourWorkflow: + @workflow.run + async def run(self, name: str) -> str: + return await workflow.execute_activity( + your_activity, + YourParams("Hello", name), + start_to_close_timeout=timedelta(seconds=10), + ) +``` + +### Develop Workflow logic {#workflow-logic-requirements} + +**How to develop Workflow logic using the Temporal Python SDK.** + +Workflow logic is constrained by [deterministic execution requirements](/workflow-definition#deterministic-constraints). +Therefore, each language is limited to the use of certain idiomatic techniques. However, each Temporal SDK provides a +set of APIs that can be used inside your Workflow to interact with external (to the Workflow) application code. + +Workflow code must be deterministic. This means: + +- no threading +- no randomness +- no external calls to processes +- no network I/O +- no global state mutation +- no system date or time + +All API safe for Workflows used in the [`temporalio.workflow`](https://python.temporal.io/temporalio.workflow.html) must +run in the implicit [`asyncio` event loop](https://docs.python.org/3/library/asyncio-eventloop.html) and be +_deterministic_. \ No newline at end of file diff --git a/docs/develop/python/cancellation.mdx b/docs/develop/python/workflows/cancellation.mdx similarity index 100% rename from docs/develop/python/cancellation.mdx rename to docs/develop/python/workflows/cancellation.mdx diff --git a/docs/develop/python/child-workflows.mdx b/docs/develop/python/workflows/child-workflows.mdx similarity index 100% rename from docs/develop/python/child-workflows.mdx rename to docs/develop/python/workflows/child-workflows.mdx diff --git a/docs/develop/python/continue-as-new.mdx b/docs/develop/python/workflows/continue-as-new.mdx similarity index 100% rename from docs/develop/python/continue-as-new.mdx rename to docs/develop/python/workflows/continue-as-new.mdx diff --git a/docs/develop/python/enriching-ui.mdx b/docs/develop/python/workflows/enriching-ui.mdx similarity index 100% rename from docs/develop/python/enriching-ui.mdx rename to docs/develop/python/workflows/enriching-ui.mdx diff --git a/docs/develop/python/workflows/index.mdx b/docs/develop/python/workflows/index.mdx new file mode 100644 index 0000000000..0f919e3a4f --- /dev/null +++ b/docs/develop/python/workflows/index.mdx @@ -0,0 +1,28 @@ +--- +id: index +title: Workflows - Python SDK +sidebar_label: Workflows +description: This section explains how to implement Workflows with the Python SDK +toc_max_heading_level: 4 +keywords: + - Python SDK +tags: + - Python SDK + - Temporal SDKs +--- + +import * as Components from '@site/src/components'; + +![Python SDK Banner](/img/assets/banner-python-temporal.png) + +## Workflows + +- [Workflow Basics](/develop/python/workflows/basics) +- [Child Workflows](/develop/python/workflows/child-workflows) +- [Continue-As-New](/develop/python/workflows/continue-as-new) +- [Cancellation](/develop/python/workflows/cancellation) +- [Timeouts](/develop/python/workflows/timeouts) +- [Message Passing](/develop/python/workflows/message-passing) +- [Enriching the UI](/develop/python/workflows/enriching-ui) +- [Schedules](/develop/python/workflows/schedules) +- [Timers](/develop/python/workflows/timers) \ No newline at end of file diff --git a/docs/develop/python/message-passing.mdx b/docs/develop/python/workflows/message-passing.mdx similarity index 99% rename from docs/develop/python/message-passing.mdx rename to docs/develop/python/workflows/message-passing.mdx index 18abc5b58d..d32eba53b1 100644 --- a/docs/develop/python/message-passing.mdx +++ b/docs/develop/python/workflows/message-passing.mdx @@ -326,7 +326,7 @@ For open source server users, Temporal Server version [Temporal Server version 1 ::: [Update-with-Start](/sending-messages#update-with-start) lets you -[send an Update](/develop/python/message-passing#send-update-from-client) that checks whether an already-running Workflow with that ID exists: +[send an Update](/develop/python/workflows/message-passing#send-update-from-client) that checks whether an already-running Workflow with that ID exists: - If the Workflow exists, the Update is processed. - If the Workflow does not exist, a new Workflow Execution is started with the given ID, and the Update is processed before the main Workflow method starts to execute. @@ -542,8 +542,8 @@ See [Finishing handlers before the Workflow completes](/handling-messages#finish ### Use `@workflow.init` to operate on Workflow input before any handler executes Normally, your Workflow `__init__` method won't have any parameters. -However, if you use the `@workflow.init` decorator on your `__init__` method, you can give it the same [Workflow parameters](/develop/python/core-application#workflow-parameters) as your `@workflow.run` method. -The SDK will then ensure that your `__init__` method receives the Workflow input arguments that the [Client sent](/develop/python/temporal-client#start-workflow-execution). +However, if you use the `@workflow.init` decorator on your `__init__` method, you can give it the same [Workflow parameters](/develop/python/workflows/basics#workflow-parameters) as your `@workflow.run` method. +The SDK will then ensure that your `__init__` method receives the Workflow input arguments that the [Client sent](/develop/python/client/temporal-client#start-workflow-execution). (The Workflow input arguments are also passed to your `@workflow.run` method -- that always happens, whether or not you use the `@workflow.init` decorator.) This is useful if you have message handlers that need access to workflow input: see [Initializing the Workflow first](/handling-messages#workflow-initializers). diff --git a/docs/develop/python/schedules.mdx b/docs/develop/python/workflows/schedules.mdx similarity index 99% rename from docs/develop/python/schedules.mdx rename to docs/develop/python/workflows/schedules.mdx index 0ade2aa7a5..b77d3f9e4e 100644 --- a/docs/develop/python/schedules.mdx +++ b/docs/develop/python/workflows/schedules.mdx @@ -3,7 +3,7 @@ id: schedules title: Schedules - Python SDK sidebar_label: Schedules description: Schedule, Create, Backfill, Delete, Describe, List, Pause, Trigger, and Update a Scheduled Workflow, along with Temporal Cron Jobs and Start Delay options. -slug: /develop/python/schedules +slug: /develop/python/workflows/schedules toc_max_heading_level: 2 keywords: - temporal python schedule workflow diff --git a/docs/develop/python/workflows/timeouts.mdx b/docs/develop/python/workflows/timeouts.mdx new file mode 100644 index 0000000000..bfbb742015 --- /dev/null +++ b/docs/develop/python/workflows/timeouts.mdx @@ -0,0 +1,91 @@ +--- +id: timeouts +title: Workflow Timeouts - Python SDK +sidebar_label: Timeouts +description: Optimize Workflow Execution with Temporal Python SDK - Set Workflow Timeouts and Retry Policies efficiently. +toc_max_heading_level: 4 +keywords: + - Python + - failure detection + - timeouts +tags: + - Activities + - Workflows + - Errors + - Failures + - Python SDK + - Temporal SDKs +--- + +## Workflow timeouts {#workflow-timeouts} + +Each Workflow timeout controls the maximum duration of a different aspect of a Workflow Execution. + +Before we continue, we want to note that we generally do not recommend setting Workflow Timeouts, because Workflows are designed to be long-running and resilient. +Instead, setting a Timeout can limit its ability to handle unexpected delays or long-running processes. +If you need to perform an action inside your Workflow after a specific period of time, we recommend using a Timer. + +Workflow timeouts are set when [starting the Workflow Execution](#workflow-timeouts). + +- **[Workflow Execution Timeout](/encyclopedia/detecting-workflow-failures#workflow-execution-timeout)** - restricts the maximum amount of time that a single Workflow Execution can be executed. +- **[Workflow Run Timeout](/encyclopedia/detecting-workflow-failures#workflow-run-timeout):** restricts the maximum amount of time that a single Workflow Run can last. +- **[Workflow Task Timeout](/encyclopedia/detecting-workflow-failures#workflow-task-timeout):** restricts the maximum amount of time that a Worker can execute a Workflow Task. + +Set the timeout to either the [`start_workflow()`](https://python.temporal.io/temporalio.client.Client.html#start_workflow) or [`execute_workflow()`](https://python.temporal.io/temporalio.client.Client.html#execute_workflow) asynchronous methods. + +Available timeouts are: + +- `execution_timeout` +- `run_timeout` +- `task_timeout` + +
+ + View the source code + {' '} + in the context of the rest of the application code. +
+ +```python +# ... + result = await client.execute_workflow( + YourWorkflow.run, + "your timeout argument", + id="your-workflow-id", + task_queue="your-task-queue", + # Set Workflow Timeout duration + execution_timeout=timedelta(seconds=2), + # run_timeout=timedelta(seconds=2), + # task_timeout=timedelta(seconds=2), + ) +``` + +## Workflow retries {#workflow-retries} + +**How to set a Workflow Retry Policy using the Temporal Python SDK** + +A Retry Policy can work in cooperation with the timeouts to provide fine controls to optimize the execution experience. + +Use a [Retry Policy](/encyclopedia/retry-policies) to retry a Workflow Execution in the event of a failure. + +Workflow Executions do not retry by default, and Retry Policies should be used with Workflow Executions only in certain situations. + +Set the Retry Policy to either the [`start_workflow()`](https://python.temporal.io/temporalio.client.Client.html#start_workflow) or [`execute_workflow()`](https://python.temporal.io/temporalio.client.Client.html#execute_workflow) asynchronous methods. + +
+ + View the source code + {' '} + in the context of the rest of the application code. +
+ +```python +# ... + handle = await client.execute_workflow( + YourWorkflow.run, + "your retry policy argument", + id="your-workflow-id", + task_queue="your-task-queue", + retry_policy=RetryPolicy(maximum_interval=timedelta(seconds=2)), + ) +``` \ No newline at end of file diff --git a/docs/develop/python/timers.mdx b/docs/develop/python/workflows/timers.mdx similarity index 97% rename from docs/develop/python/timers.mdx rename to docs/develop/python/workflows/timers.mdx index 99187fc422..bf7a1118b2 100644 --- a/docs/develop/python/timers.mdx +++ b/docs/develop/python/workflows/timers.mdx @@ -3,7 +3,7 @@ id: timers title: Durable Timers - Python SDK sidebar_label: Durable Timers description: Set durable Timers with Temporal Workflows using sleep() or timer(), ensuring code execution resumes after downtime. Sleep for months using resource-light operations in Python. -slug: /develop/python/timers +slug: /develop/python/workflows/timers toc_max_heading_level: 2 keywords: - temporal workflow timers diff --git a/docs/develop/python/versioning.mdx b/docs/develop/python/workflows/versioning.mdx similarity index 99% rename from docs/develop/python/versioning.mdx rename to docs/develop/python/workflows/versioning.mdx index 192a2562f6..4900ab5698 100644 --- a/docs/develop/python/versioning.mdx +++ b/docs/develop/python/workflows/versioning.mdx @@ -3,7 +3,7 @@ id: versioning title: Versioning - Python SDK sidebar_label: Versioning description: Ensure deterministic Temporal Workflow execution and safely deploy updates using the Python SDK's patching and Worker Versioning APIs, for scalable long-running Workflows. -slug: /develop/python/versioning +slug: /develop/python/workflows/versioning toc_max_heading_level: 4 keywords: - best practices @@ -260,4 +260,4 @@ This method also does not provide a way to version any still-running Workflows - ### Testing a Workflow for replay safety -To determine whether your Workflow your needs a patch, or that you've patched it successfully, you should incorporate [Replay Testing](/develop/python/testing-suite#replay). +To determine whether your Workflow your needs a patch, or that you've patched it successfully, you should incorporate [Replay Testing](/develop/python/best-practices/testing-suite#replay). diff --git a/docs/develop/python/worker-versioning-legacy.mdx b/docs/develop/python/workflows/worker-versioning-legacy.mdx similarity index 64% rename from docs/develop/python/worker-versioning-legacy.mdx rename to docs/develop/python/workflows/worker-versioning-legacy.mdx index c59fccc023..9dd79fe3d6 100644 --- a/docs/develop/python/worker-versioning-legacy.mdx +++ b/docs/develop/python/workflows/worker-versioning-legacy.mdx @@ -2,7 +2,7 @@ id: worker-versioning-legacy title: Worker Versioning (Legacy) - Python SDK description: Learn the Python SDK's outdated Worker Versioning APIs. -slug: /develop/python/worker-versioning-legacy +slug: /develop/python/workflows/worker-versioning-legacy toc_max_heading_level: 2 keywords: - deprecated @@ -16,19 +16,23 @@ tags: :::caution -This section is for a deprecated Worker Versioning API. Please redirect your attention to [Worker Versioning](/production-deployment/worker-deployments/worker-versioning). +This section is for a deprecated Worker Versioning API. Please redirect your attention to +[Worker Versioning](/production-deployment/worker-deployments/worker-versioning). -See the [Pre-release README](https://github.com/temporalio/temporal/blob/main/docs/worker-versioning.md) for more information. +See the [Pre-release README](https://github.com/temporalio/temporal/blob/main/docs/worker-versioning.md) for more +information. ::: -A Build ID corresponds to a deployment. If you don't already have one, we recommend a hash of the code--such as a Git SHA--combined with a human-readable timestamp. -To use Worker Versioning, you need to pass a Build ID to your Java Worker and opt in to Worker Versioning. +A Build ID corresponds to a deployment. If you don't already have one, we recommend a hash of the code--such as a Git +SHA--combined with a human-readable timestamp. To use Worker Versioning, you need to pass a Build ID to your Java Worker +and opt in to Worker Versioning. ### Assign a Build ID to your Worker and opt in to Worker Versioning -You should understand assignment rules before completing this step. -See the [Worker Versioning Pre-release README](https://github.com/temporalio/temporal/blob/main/docs/worker-versioning.md) for more information. +You should understand assignment rules before completing this step. See the +[Worker Versioning Pre-release README](https://github.com/temporalio/temporal/blob/main/docs/worker-versioning.md) for +more information. To enable Worker Versioning for your Worker, assign the Build ID--perhaps from an environment variable--and turn it on. @@ -53,14 +57,17 @@ Importantly, when you start this Worker, it won't receive any tasks until you se :::caution -This section is for a deprecated Worker Versioning API. Please redirect your attention to [Worker Versioning](/production-deployment/worker-deployments/worker-versioning). +This section is for a deprecated Worker Versioning API. Please redirect your attention to +[Worker Versioning](/production-deployment/worker-deployments/worker-versioning). ::: -By default, Activities, Child Workflows, and Continue-as-New Workflows are run on the build of the workflow that created them if they are also configured to run on the same Task Queue. -When configured to run on a separate Task Queue, they will default to using the current assignment rules. +By default, Activities, Child Workflows, and Continue-as-New Workflows are run on the build of the workflow that created +them if they are also configured to run on the same Task Queue. When configured to run on a separate Task Queue, they +will default to using the current assignment rules. -If you want to override this behavior, you can specify your intent via the `versioning_intent` argument available on the methods you use to invoke these commands. +If you want to override this behavior, you can specify your intent via the `versioning_intent` argument available on the +methods you use to invoke these commands. For example, if you want an Activity to use the latest assignment rules rather than inheriting from its parent: @@ -79,12 +86,13 @@ await workflow.execute_activity( :::caution -This section is for a deprecated Worker Versioning API. Please redirect your attention to [Worker Versioning](/production-deployment/worker-deployments/worker-versioning). +This section is for a deprecated Worker Versioning API. Please redirect your attention to +[Worker Versioning](/production-deployment/worker-deployments/worker-versioning). ::: -Now you can use the SDK (or the Temporal CLI) to tell the Task Queue about your Worker's Build ID. -You might want to do this as part of your CI deployment process. +Now you can use the SDK (or the Temporal CLI) to tell the Task Queue about your Worker's Build ID. You might want to do +this as part of your CI deployment process. ```python # ... @@ -93,8 +101,9 @@ await client.update_worker_build_id_compatibility( ) ``` -This code adds the `deadbeef` Build ID to the Task Queue as the sole version in a new version set, which becomes the default for the queue. -New Workflows execute on Workers with this Build ID, and existing ones will continue to process by appropriately compatible Workers. +This code adds the `deadbeef` Build ID to the Task Queue as the sole version in a new version set, which becomes the +default for the queue. New Workflows execute on Workers with this Build ID, and existing ones will continue to process +by appropriately compatible Workers. If, instead, you want to add the Build ID to an existing compatible set, you can do this: @@ -105,7 +114,8 @@ await client.update_worker_build_id_compatibility( ) ``` -This code adds `deadbeef` to the existing compatible set containing `some-existing-build-id` and marks it as the new default Build ID for that set. +This code adds `deadbeef` to the existing compatible set containing `some-existing-build-id` and marks it as the new +default Build ID for that set. You can also promote an existing Build ID in a set to be the default for that set: @@ -116,7 +126,8 @@ await client.update_worker_build_id_compatibility( ) ``` -You can also promote an entire set to become the default set for the queue. New Workflows will start using that set's default build. +You can also promote an entire set to become the default set for the queue. New Workflows will start using that set's +default build. ```python # ... diff --git a/docs/develop/ruby/temporal-client.mdx b/docs/develop/ruby/temporal-client.mdx index e97ac21a5e..d985753d1b 100644 --- a/docs/develop/ruby/temporal-client.mdx +++ b/docs/develop/ruby/temporal-client.mdx @@ -51,7 +51,7 @@ configure multiple profiles, each with its own set of connection options. You ca creating the Temporal Client. You can use the environment variable `TEMPORAL_CONFIG_FILE` to specify the location of the TOML file or provide the path to the file directly in code. If you don't provide the configuration file path, the SDK looks for it at the path `~/.config/temporalio/temporal.toml` or the equivalent on your OS. Refer to -[Environment Configuration](../environment-configuration.mdx#configuration-methods) for more details about configuration +[Environment Configuration](/develop/environment-configuration.mdx#configuration-methods) for more details about configuration files and profiles. :::info diff --git a/docs/encyclopedia/activities/activity-definition.mdx b/docs/encyclopedia/activities/activity-definition.mdx index 5acbfba998..0d97c3e4e6 100644 --- a/docs/encyclopedia/activities/activity-definition.mdx +++ b/docs/encyclopedia/activities/activity-definition.mdx @@ -36,7 +36,7 @@ Activities encapsulate business logic that is prone to failure, allowing for aut - [How to develop an Activity Definition using the Go SDK](/develop/go/core-application#activity-definition) - [How to develop an Activity Definition using the Java SDK](/develop/java/core-application#develop-activities) - [How to develop an Activity Definition using the PHP SDK](/develop/php/core-application#develop-activities) -- [How to develop an Activity Definition using the Python SDK](/develop/python/core-application#develop-activities) +- [How to develop an Activity Definition using the Python SDK](/develop/python/activities/basics) - [How to develop an Activity Definition using the TypeScript SDK](/develop/typescript/core-application#develop-activities) - [How to develop an Activity Definition using the .NET SDK](/develop/dotnet/core-application#develop-activity) @@ -154,5 +154,5 @@ Here are some best practices you can use when you are creating an Activities for - Activity arguments and return values should be serializable. - Activities that perform writes should be idempotent. -- Activities have [timeouts](/develop/python/failure-detection#heartbeat-timeout) and [retry policies](/encyclopedia/retry-policies). For Activities, your operation should either complete within a few minutes or it should support the ability to heartbeat or poll for a result. This way it will be clear to the Workflow when the Activity is still making progress. +- Activities have [timeouts](/develop/python/activities/timeouts#activity-heartbeats) and [retry policies](/encyclopedia/retry-policies). For Activities, your operation should either complete within a few minutes or it should support the ability to heartbeat or poll for a result. This way it will be clear to the Workflow when the Activity is still making progress. - You need to specify at least one timeout, typically the [start_to_close timeout](/encyclopedia/detecting-activity-failures#start-to-close-timeout). Keep in mind that the shorter the timeout, the faster Temporal will retry upon failure. See the [Activity retry policy section](#activity-retry-policy) to learn more. diff --git a/docs/encyclopedia/activities/activity-execution.mdx b/docs/encyclopedia/activities/activity-execution.mdx index 5696d66a19..f5f33aaf42 100644 --- a/docs/encyclopedia/activities/activity-execution.mdx +++ b/docs/encyclopedia/activities/activity-execution.mdx @@ -34,7 +34,7 @@ An Activity Execution is the full chain of [Activity Task Executions](/tasks#act - [How to start an Activity Execution using the Go SDK](/develop/go/core-application#activity-execution) - [How to start an Activity Execution using the Java SDK](/develop/java/core-application#activity-execution) - [How to start an Activity Execution using the PHP SDK](/develop/php/core-application#activity-execution) -- [How to start an Activity Execution using the Python SDK](/develop/python/core-application#activity-execution) +- [How to start an Activity Execution using the Python SDK](/develop/python/activities/execution) - [How to start an Activity Execution using the TypeScript SDK](/develop/typescript/core-application#activity-execution) - [How to start an Activity Execution using the .NET SDK](/develop/dotnet/core-application#activity-execution) @@ -124,7 +124,7 @@ How to complete an Activity Asynchronously in: - [Go](/develop/go/asynchronous-activity-completion) - [Java](/develop/java/asynchronous-activity-completion) - [PHP](/develop/php/asynchronous-activity-completion) -- [Python](/develop/python/asynchronous-activity-completion) +- [Python](/develop/python/activities/asynchronous-activity-completion) - [TypeScript](/develop/typescript/asynchronous-activity-completion) - [.NET](/develop/dotnet/asynchronous-activity) diff --git a/docs/encyclopedia/child-workflows/child-workflows.mdx b/docs/encyclopedia/child-workflows/child-workflows.mdx index c6881a77ee..0bcaf65d7b 100644 --- a/docs/encyclopedia/child-workflows/child-workflows.mdx +++ b/docs/encyclopedia/child-workflows/child-workflows.mdx @@ -33,7 +33,7 @@ A Child Workflow Execution is a [Workflow Execution](/workflow-execution) that i - [Go SDK Child Workflow feature guide](/develop/go/child-workflows) - [Java SDK Child Workflow feature guide](/develop/java/child-workflows) - [PHP SDK Child Workflow feature guide](/develop/php/child-workflows) -- [Python SDK Child Workflow feature guide](/develop/python/child-workflows) +- [Python SDK Child Workflow feature guide](/develop/python/workflows/child-workflows) - [TypeScript SDK Child Workflow feature guide](/develop/typescript/child-workflows) - [.NET SDK Child Workflow feature guide](/develop/dotnet/child-workflows) diff --git a/docs/encyclopedia/child-workflows/parent-close-policy.mdx b/docs/encyclopedia/child-workflows/parent-close-policy.mdx index 8f75186071..8619140703 100644 --- a/docs/encyclopedia/child-workflows/parent-close-policy.mdx +++ b/docs/encyclopedia/child-workflows/parent-close-policy.mdx @@ -38,7 +38,7 @@ A Parent Close Policy determines what happens to a Child Workflow Execution if i - [How to set a Parent Close Policy using the Go SDK](/develop/go/child-workflows#parent-close-policy) - [How to set a Parent Close Policy using the Java SDK](/develop/java/child-workflows#parent-close-policy) - [How to set a Parent Close Policy using the PHP SDK](/develop/php/child-workflows#parent-close-policy) -- [How to set a Parent Close Policy using the Python SDK](/develop/python/child-workflows#parent-close-policy) +- [How to set a Parent Close Policy using the Python SDK](/develop/python/workflows/child-workflows#parent-close-policy) - [How to set a Parent Close Policy using the TypeScript SDK](/develop/typescript/child-workflows#parent-close-policy) - [How to set a Parent Close Policy using the .NET SDK](/develop/dotnet/child-workflows#parent-close-policy) diff --git a/docs/encyclopedia/detecting-activity-failures.mdx b/docs/encyclopedia/detecting-activity-failures.mdx index 9a2355d719..2b5072ffa1 100644 --- a/docs/encyclopedia/detecting-activity-failures.mdx +++ b/docs/encyclopedia/detecting-activity-failures.mdx @@ -35,7 +35,7 @@ In other words, it's a limit for how long an Activity Task can be enqueued. - + @@ -82,7 +82,7 @@ A Start-To-Close Timeout is the maximum time allowed for a single [Activity Task - + @@ -134,7 +134,7 @@ A Schedule-To-Close Timeout is the maximum amount of time allowed for the overal - + @@ -174,7 +174,7 @@ Each ping informs the Temporal Service that the Activity Execution is making pro - + @@ -256,7 +256,7 @@ A Heartbeat Timeout is the maximum time between [Activity Heartbeats](#activity- - + diff --git a/docs/encyclopedia/detecting-workflow-failures.mdx b/docs/encyclopedia/detecting-workflow-failures.mdx index 0bd8267c03..7bd3b0685a 100644 --- a/docs/encyclopedia/detecting-workflow-failures.mdx +++ b/docs/encyclopedia/detecting-workflow-failures.mdx @@ -37,7 +37,7 @@ A Workflow Execution Timeout is the maximum time that a Workflow Execution can b - + @@ -65,7 +65,7 @@ A Workflow Run Timeout restricts the maximum duration of a single Workflow Run. - + @@ -91,7 +91,7 @@ This Timeout is primarily available to recognize whether a Worker has gone down - + diff --git a/docs/encyclopedia/event-history/python.mdx b/docs/encyclopedia/event-history/python.mdx index 67d29144b0..2cd3f8f89a 100644 --- a/docs/encyclopedia/event-history/python.mdx +++ b/docs/encyclopedia/event-history/python.mdx @@ -312,7 +312,7 @@ considered a [Workflow Task Failure](https://docs.temporal.io/references/failure considered a transient failure, meaning it retries over and over. Users can also fix the source of non-determinism, perhaps by removing the Activity, and then restart the Workers. This means that this type of failure can recover by itself. You can also use a strategy called versioning to address this non-determinism error. See -[versioning](https://docs.temporal.io/develop/python/versioning) to learn more. +[versioning](/develop/python/workflows/versioning) to learn more. For more information on how Temporal handles Durable Execution or to see these slides in a video format with more explanation, check out our free, self-paced courses: [Temporal 102](https://learn.temporal.io/courses/temporal_102/) and diff --git a/docs/encyclopedia/retry-policies.mdx b/docs/encyclopedia/retry-policies.mdx index ae019e3559..721404f5db 100644 --- a/docs/encyclopedia/retry-policies.mdx +++ b/docs/encyclopedia/retry-policies.mdx @@ -62,7 +62,7 @@ Try out the [Activity retry simulator](/develop/activity-retry-simulator) to vis archetype="feature-guide" /> @@ -92,7 +92,7 @@ Try out the [Activity retry simulator](/develop/activity-retry-simulator) to vis archetype="feature-guide" /> diff --git a/docs/encyclopedia/temporal-sdks.mdx b/docs/encyclopedia/temporal-sdks.mdx index 36bef5eaa7..cc51bde215 100644 --- a/docs/encyclopedia/temporal-sdks.mdx +++ b/docs/encyclopedia/temporal-sdks.mdx @@ -184,7 +184,7 @@ For tested code samples and best practices, use your preferred language SDK's de - [Go SDK Temporal Client feature guide](/develop/go/temporal-client) - [Java SDK Temporal Client feature guide](/develop/java/temporal-client) - [PHP SDK Temporal Client feature guide](/develop/php/temporal-client) -- [Python SDK Temporal Client feature guide](/develop/python/temporal-client) +- [Python SDK Temporal Client feature guide](/develop/python/client/temporal-client) - [TypeScript SDK Temporal Client feature guide](/develop/typescript/core-application) ::: diff --git a/docs/encyclopedia/visibility/search-attributes.mdx b/docs/encyclopedia/visibility/search-attributes.mdx index 264027c984..96d2132b21 100644 --- a/docs/encyclopedia/visibility/search-attributes.mdx +++ b/docs/encyclopedia/visibility/search-attributes.mdx @@ -216,7 +216,7 @@ With Workflows you can do the following: - [How to manage Search Attributes using the Go SDK](/develop/go/observability#visibility) - [How to manage Search Attributes using the Java SDK](/develop/java/observability#visibility) - [How to manage Search Attributes using the PHP SDK](/develop/php/observability#visibility) -- [How to manage Search Attributes using the Python SDK](/develop/python/observability#visibility) +- [How to manage Search Attributes using the Python SDK](/develop/python/workers/observability#visibility) - [How to manage Search Attributes using the TypeScript SDK](/develop/typescript/observability#visibility) - [How to manage Search Attributes using the .NET SDK](/develop/dotnet/observability#search-attributes) diff --git a/docs/encyclopedia/workers/task-queues.mdx b/docs/encyclopedia/workers/task-queues.mdx index f68072c482..3e7a0fa2ce 100644 --- a/docs/encyclopedia/workers/task-queues.mdx +++ b/docs/encyclopedia/workers/task-queues.mdx @@ -82,7 +82,7 @@ There are five places where the name of the Task Queue can be set by the develop - [How to start a Workflow Execution using the Go SDK](/develop/go/temporal-client#start-workflow-execution) - [How to start a Workflow Execution using the Java SDK](/develop/java/temporal-client#start-workflow-execution) - [How to start a Workflow Execution using the PHP SDK](/develop/php/temporal-client#start-workflow-execution) - - [How to start a Workflow Execution using the Python SDK](/develop/python/temporal-client#start-workflow-execution) + - [How to start a Workflow Execution using the Python SDK](/develop/python/client/temporal-client#start-workflow-execution) - [How to start a Workflow Execution using the TypeScript SDK](/develop/typescript/temporal-client#start-workflow-execution) - [How to start a Workflow Execution using the .NET SDK](/develop/dotnet/temporal-client#start-workflow) @@ -91,7 +91,7 @@ There are five places where the name of the Task Queue can be set by the develop - [How to run a development Worker using the Go SDK](/develop/go/core-application#develop-worker) - [How to run a development Worker using the Java SDK](/develop/java/core-application#run-a-dev-worker) - [How to run a development Worker using the PHP SDK](/develop/php/core-application#run-a-dev-worker) - - [How to run a development Worker using the Python SDK](/develop/python/core-application#run-a-dev-worker) + - [How to run a development Worker using the Python SDK](/develop/python/workers/run-worker-process#run-a-dev-worker) - [How to run a development Worker using the TypeScript SDK](/develop/typescript/core-application#run-a-dev-worker) - [How to run a development Worker using the .NET SDK](/develop/dotnet/core-application#run-worker-process)

- [How to run a Temporal Cloud Worker using the Go SDK](/develop/go/core-application#run-a-temporal-cloud-worker) @@ -110,7 +110,7 @@ There are five places where the name of the Task Queue can be set by the develop - [How to start an Activity Execution using the Go SDK](/develop/go/core-application#activity-execution) - [How to start an Activity Execution using the Java SDK](/develop/java/core-application#activity-execution) - [How to start an Activity Execution using the PHP SDK](/develop/php/core-application#activity-execution) - - [How to start an Activity Execution using the Python SDK](/develop/python/core-application#activity-execution) + - [How to start an Activity Execution using the Python SDK](/develop/python/activities/execution) - [How to start an Activity Execution using the TypeScript SDK](/develop/typescript/core-application#activity-execution) - [How to start an Activity Execution using the .NET SDK](/develop/dotnet/core-application#activity-execution) @@ -122,7 +122,7 @@ There are five places where the name of the Task Queue can be set by the develop - [How to start a Child Workflow Execution using the Go SDK](/develop/go/child-workflows) - [How to start a Child Workflow Execution using the Java SDK](/develop/java/child-workflows) - [How to start a Child Workflow Execution using the PHP SDK](/develop/php/continue-as-new) - - [How to start a Child Workflow Execution using the Python SDK](/develop/python/child-workflows) + - [How to start a Child Workflow Execution using the Python SDK](/develop/python/workflows/child-workflows) - [How to start a Child Workflow Execution using the TypeScript SDK](/develop/typescript/child-workflows) - [How to start a Child Workflow Execution using the .NET SDK](/develop/dotnet/child-workflows) diff --git a/docs/encyclopedia/workers/workers.mdx b/docs/encyclopedia/workers/workers.mdx index 72d4529bd8..b018e7b656 100644 --- a/docs/encyclopedia/workers/workers.mdx +++ b/docs/encyclopedia/workers/workers.mdx @@ -37,7 +37,7 @@ A Worker Program is the static code that defines the constraints of the Worker P - [How to run a development Worker using the Go SDK](/develop/go/core-application#develop-worker) - [How to run a development Worker using the Java SDK](/develop/java/core-application#run-a-dev-worker) - [How to run a development Worker using the PHP SDK](/develop/php/core-application#run-a-dev-worker) -- [How to run a development Worker using the Python SDK](/develop/python/core-application#run-a-dev-worker) +- [How to run a development Worker using the Python SDK](/develop/python/workers/run-worker-process#run-a-dev-worker) - [How to run a development Worker using the TypeScript SDK](/develop/typescript/core-application#run-a-dev-worker) - [How to run a development Worker using the .NET SDK](/develop/dotnet/core-application#run-worker-process) diff --git a/docs/encyclopedia/workflow-message-passing/handling-messages.mdx b/docs/encyclopedia/workflow-message-passing/handling-messages.mdx index 5987ce4675..2c9550eac9 100644 --- a/docs/encyclopedia/workflow-message-passing/handling-messages.mdx +++ b/docs/encyclopedia/workflow-message-passing/handling-messages.mdx @@ -132,7 +132,7 @@ See examples of the above patterns. - + @@ -155,7 +155,7 @@ Once the Update handler is finished and has returned a value, the operation is c - + @@ -198,7 +198,7 @@ Use these links to see a simple Signal handler. - + @@ -211,7 +211,7 @@ Use these links to see a simple update handler. - + @@ -224,7 +224,7 @@ Author queries using these per-language guides. - + diff --git a/docs/encyclopedia/workflow-message-passing/sending-messages.mdx b/docs/encyclopedia/workflow-message-passing/sending-messages.mdx index 70deb90c48..f0859e0c5b 100644 --- a/docs/encyclopedia/workflow-message-passing/sending-messages.mdx +++ b/docs/encyclopedia/workflow-message-passing/sending-messages.mdx @@ -51,7 +51,7 @@ You can also Signal-With-Start to lazily initialize a Workflow while sending a S - + @@ -62,7 +62,7 @@ You can also Signal-With-Start to lazily initialize a Workflow while sending a S - + @@ -75,7 +75,7 @@ Signal-With-Start is a great tool for lazily initializing Workflows. When you se - + @@ -117,7 +117,7 @@ Use [Update Validators](/handling-messages#update-validators) and [Update IDs](/ - + @@ -165,7 +165,7 @@ The SDKs will retry the Update-With-Start request, but there is no guarantee tha - + @@ -180,7 +180,7 @@ You can also send a built-in "Stack Trace Query" for debugging. - + diff --git a/docs/encyclopedia/workflow/cron-job.mdx b/docs/encyclopedia/workflow/cron-job.mdx index 241f08596e..25b10ff91e 100644 --- a/docs/encyclopedia/workflow/cron-job.mdx +++ b/docs/encyclopedia/workflow/cron-job.mdx @@ -31,7 +31,7 @@ A Temporal Cron Job is the series of Workflow Executions that occur when a Cron - [How to set a Cron Schedule using the Go SDK](/develop/go/schedules#temporal-cron-jobs) - [How to set a Cron Schedule using the Java SDK](/develop/java/schedules#cron-schedule) - [How to set a Cron Schedule using the PHP SDK](/develop/php/schedules#temporal-cron-jobs) -- [How to set a Cron Schedule using the Python SDK](/develop/python/schedules#temporal-cron-jobs) +- [How to set a Cron Schedule using the Python SDK](/develop/python/workflows/schedules#temporal-cron-jobs) - [How to set a Cron Schedule using the TypeScript SDK](/develop/typescript/schedules#temporal-cron-jobs) -**[Workflow Definition in Python](/develop/python/core-application#develop-workflows)** +**[Workflow Definition in Python](/develop/python/workflows/basics)** ```Python @workflow.defn @@ -289,7 +289,7 @@ To patch: - [How to patch Workflow code in Go](/develop/go/versioning#patching) - [How to patch Workflow code in Java](/develop/java/versioning#patching) -- [How to patch Workflow code in Python](/develop/python/versioning#patching) +- [How to patch Workflow code in Python](/develop/python/workflows/versioning#patching) - [How to patch Workflow code in PHP](/develop/php/versioning#php-sdk-patching-api) - [How to patch Workflow code in TypeScript](/develop/typescript/versioning#patching) - [How to patch Workflow code in .NET](/develop/dotnet/versioning#patching) diff --git a/docs/encyclopedia/workflow/workflow-execution/continue-as-new.mdx b/docs/encyclopedia/workflow/workflow-execution/continue-as-new.mdx index 9ef1ff61e4..d28ff00a3b 100644 --- a/docs/encyclopedia/workflow/workflow-execution/continue-as-new.mdx +++ b/docs/encyclopedia/workflow/workflow-execution/continue-as-new.mdx @@ -39,7 +39,7 @@ Workflows that do this are often called Entity Workflows because they represent - [How to Continue-As-New using the Go SDK](/develop/go/continue-as-new#how) - [How to Continue-As-New using the Java SDK](/develop/java/continue-as-new) - [How to Continue-As-New using the PHP SDK](/develop/php/continue-as-new) -- [How to Continue-As-New using the Python SDK](/develop/python/continue-as-new#how) +- [How to Continue-As-New using the Python SDK](/develop/python/workflows/continue-as-new#how) - [How to Continue-As-New using the TypeScript SDK](/develop/typescript/continue-as-new) - [How to Continue-As-New using the .NET SDK](/develop/dotnet/continue-as-new) @@ -53,6 +53,6 @@ To prevent long-running Workflows from running on stale versions of code, you ma - [Determine when to Continue-As-New using the Go SDK](/develop/go/continue-as-new#when) - [Determine when to Continue-As-New using the Java SDK](/develop/java/continue-as-new) - [Determine when to Continue-As-New using the PHP SDK](/develop/php/continue-as-new) -- [Determine when to Continue-As-New using the Python SDK](/develop/python/continue-as-new#when) +- [Determine when to Continue-As-New using the Python SDK](/develop/python/workflows/continue-as-new#when) - [Determine when to Continue-As-New using the TypeScript SDK](/develop/typescript/continue-as-new) - [Determine when to Continue-As-New using the .NET SDK](/develop/dotnet/continue-as-new) diff --git a/docs/encyclopedia/workflow/workflow-execution/timers-delays.mdx b/docs/encyclopedia/workflow/workflow-execution/timers-delays.mdx index 2da07e0453..cadd879a3d 100644 --- a/docs/encyclopedia/workflow/workflow-execution/timers-delays.mdx +++ b/docs/encyclopedia/workflow/workflow-execution/timers-delays.mdx @@ -26,7 +26,7 @@ Workers consume no additional resources while waiting for a Timer to fire, so a - [How to set Timers in Go](/develop/go/timers) - [How to set Timers in Java](/develop/java/timers) - [How to set Timers in PHP](/develop/php/timers) -- [How to set Timers in Python](/develop/python/timers) +- [How to set Timers in Python](/develop/python/workflows/timers) - [How to set Timers in TypeScript](/develop/typescript/timers) - [How to set Timers in .NET](/develop/dotnet/durable-timers) diff --git a/docs/encyclopedia/workflow/workflow-execution/workflow-execution.mdx b/docs/encyclopedia/workflow/workflow-execution/workflow-execution.mdx index 6c334226b4..bebd10328c 100644 --- a/docs/encyclopedia/workflow/workflow-execution/workflow-execution.mdx +++ b/docs/encyclopedia/workflow/workflow-execution/workflow-execution.mdx @@ -36,7 +36,7 @@ It is the main unit of execution of a [Temporal Application](/temporal#temporal- - [How to start a Workflow Execution using the Go SDK](/develop/go/temporal-client#start-workflow-execution) - [How to start a Workflow Execution using the Java SDK](/develop/java/temporal-client#start-workflow-execution) - [How to start a Workflow Execution using the PHP SDK](/develop/php/temporal-client#start-workflow-execution) -- [How to start a Workflow Execution using the Python SDK](/develop/python/temporal-client#start-workflow-execution) +- [How to start a Workflow Execution using the Python SDK](/develop/python/client/temporal-client#start-workflow-execution) - [How to start a Workflow Execution using the TypeScript SDK](/develop/typescript/temporal-client#start-workflow-execution) - [How to start a Workflow Execution using the .NET SDK](/develop/dotnet/temporal-client#start-workflow) @@ -74,7 +74,7 @@ If a failure occurs, the Workflow Execution picks up where the last recorded eve - [How to use Replay APIs using the Go SDK](/develop/go/testing-suite#replay) - [How to use Replay APIs using the Java SDK](/develop/java/testing-suite#replay) -- [How to use Replay APIs using the Python SDK](/develop/python/testing-suite#replay) +- [How to use Replay APIs using the Python SDK](/develop/python/best-practices/testing-suite#replay) - [How to use Replay APIs using the TypeScript SDK](/develop/typescript/testing-suite#replay) - [How to use Replay APIs using the .NET SDK](/develop/dotnet/testing-suite#replay) diff --git a/docs/evaluate/development-production-features/core-application.mdx b/docs/evaluate/development-production-features/core-application.mdx index 632c860040..21c4adb5bf 100644 --- a/docs/evaluate/development-production-features/core-application.mdx +++ b/docs/evaluate/development-production-features/core-application.mdx @@ -48,7 +48,7 @@ Or jump straight to a Temporal SDK feature guide: - + diff --git a/docs/evaluate/development-production-features/data-encryption.mdx b/docs/evaluate/development-production-features/data-encryption.mdx index 7047f311dd..b10b753807 100644 --- a/docs/evaluate/development-production-features/data-encryption.mdx +++ b/docs/evaluate/development-production-features/data-encryption.mdx @@ -31,7 +31,7 @@ Jump straight to a Temporal SDK feature guide. - + diff --git a/docs/evaluate/development-production-features/debugging.mdx b/docs/evaluate/development-production-features/debugging.mdx index ad6beaebd7..ac27499478 100644 --- a/docs/evaluate/development-production-features/debugging.mdx +++ b/docs/evaluate/development-production-features/debugging.mdx @@ -36,7 +36,7 @@ Jump straight to a Temporal SDK feature guide. - + diff --git a/docs/evaluate/development-production-features/failure-detection.mdx b/docs/evaluate/development-production-features/failure-detection.mdx index 9ed8340540..e72a09bcf5 100644 --- a/docs/evaluate/development-production-features/failure-detection.mdx +++ b/docs/evaluate/development-production-features/failure-detection.mdx @@ -44,7 +44,7 @@ Or jump straight to a Temporal SDK feature guide. - + @@ -54,7 +54,7 @@ Or jump straight to a Temporal SDK feature guide. - + diff --git a/docs/evaluate/development-production-features/interrupt-a-workflow.mdx b/docs/evaluate/development-production-features/interrupt-a-workflow.mdx index 3732d4e353..90e4ba94cb 100644 --- a/docs/evaluate/development-production-features/interrupt-a-workflow.mdx +++ b/docs/evaluate/development-production-features/interrupt-a-workflow.mdx @@ -50,7 +50,7 @@ robust and responsive. archetype="feature-guide" /> diff --git a/docs/evaluate/development-production-features/observability.mdx b/docs/evaluate/development-production-features/observability.mdx index e781938868..ee890eef5f 100644 --- a/docs/evaluate/development-production-features/observability.mdx +++ b/docs/evaluate/development-production-features/observability.mdx @@ -55,7 +55,7 @@ Jump straight into the Temporal SDK feature guide. - + diff --git a/docs/evaluate/development-production-features/schedules.mdx b/docs/evaluate/development-production-features/schedules.mdx index 5a0955cd33..980492b561 100644 --- a/docs/evaluate/development-production-features/schedules.mdx +++ b/docs/evaluate/development-production-features/schedules.mdx @@ -39,7 +39,7 @@ Jump straight to a Temporal SDK feature guide. - + diff --git a/docs/evaluate/development-production-features/testing-suite.mdx b/docs/evaluate/development-production-features/testing-suite.mdx index f3f8b0cd2e..adbd0e672f 100644 --- a/docs/evaluate/development-production-features/testing-suite.mdx +++ b/docs/evaluate/development-production-features/testing-suite.mdx @@ -40,7 +40,7 @@ Jump straight to a Temporal SDK feature guide. - + diff --git a/docs/evaluate/development-production-features/throughput-composability.mdx b/docs/evaluate/development-production-features/throughput-composability.mdx index 1c11f5d923..474b7cf6c9 100644 --- a/docs/evaluate/development-production-features/throughput-composability.mdx +++ b/docs/evaluate/development-production-features/throughput-composability.mdx @@ -35,7 +35,7 @@ See the SDK feature guides for implementation details: - + diff --git a/docs/evaluate/development-production-features/workflow-message-passing.mdx b/docs/evaluate/development-production-features/workflow-message-passing.mdx index 5220949f8d..946c1654f6 100644 --- a/docs/evaluate/development-production-features/workflow-message-passing.mdx +++ b/docs/evaluate/development-production-features/workflow-message-passing.mdx @@ -55,7 +55,7 @@ If you want to jump to straight to implementation details, see the SDK feature g - + diff --git a/docs/production-deployment/self-hosted-guide/monitoring.mdx b/docs/production-deployment/self-hosted-guide/monitoring.mdx index cb1e4b978f..a850ead13a 100644 --- a/docs/production-deployment/self-hosted-guide/monitoring.mdx +++ b/docs/production-deployment/self-hosted-guide/monitoring.mdx @@ -96,7 +96,7 @@ The Metrics section in the Observability guide details how to create hooks for a - [Go](/develop/go/observability#metrics) - [Java](/develop/java/observability#metrics) - [PHP](/develop/php/observability) -- [Python](/develop/python/observability#metrics) +- [Python](/develop/python/workers/observability#metrics) - [TypeScript](/develop/typescript/observability#metrics) - [.NET](/develop/dotnet/observability#metrics) - [Ruby](/develop/ruby/observability#metrics) diff --git a/docs/references/sdk-metrics.mdx b/docs/references/sdk-metrics.mdx index c87792e0fa..0b685f13e3 100644 --- a/docs/references/sdk-metrics.mdx +++ b/docs/references/sdk-metrics.mdx @@ -29,7 +29,7 @@ The Temporal SDKs emit a set of metrics from Temporal Client usage and Worker Pr - [How to emit metrics using the Go SDK](/develop/go/observability#metrics) - [How to emit metrics using the Java SDK](/develop/java/observability#metrics) -- [How to emit metrics using the Python SDK](/develop/python/observability#metrics) +- [How to emit metrics using the Python SDK](/develop/python/workers/observability#metrics) - [How to emit metrics using the TypeScript SDK](/develop/typescript/observability#metrics) - [How to emit metrics using the .NET SDK](/develop/dotnet/observability#metrics) - [How to emit metrics using the Ruby SDK](/develop/ruby/observability#metrics) diff --git a/docs/web-ui.mdx b/docs/web-ui.mdx index aee2192560..1f0f0ff07d 100644 --- a/docs/web-ui.mdx +++ b/docs/web-ui.mdx @@ -259,7 +259,7 @@ To read more about Schedules, explore these links: - +