@@ -9,16 +9,36 @@ execute asynchronous long-running business logic in a scalable and resilient way
99
1010"Temporal Python SDK" is the framework for authoring workflows and activities using the Python programming language.
1111
12- In addition to this documentation, see the [ samples] ( https://github.com/temporalio/samples-python ) repository for code
13- examples.
12+ Also see:
13+
14+ * [ Code Samples] ( https://github.com/temporalio/samples-python )
15+ * [ API Documentation] ( https://python.temporal.io )
16+
17+ In addition to features common across all Temporal SDKs, the Python SDK also has the following interesting features:
18+
19+ ** Type Safe**
20+
21+ This library uses the latest typing and MyPy support with generics to ensure all calls can be typed. For example,
22+ starting a workflow with an ` int ` parameter when it accepts a ` str ` parameter would cause MyPy to fail.
23+
24+ ** Different Activity Types**
25+
26+ The activity worker has been developed to work with ` async def ` , threaded, and multiprocess activities. While
27+ ` async def ` activities are the easiest and recommended, care has been taken to make heartbeating and cancellation also
28+ work across threads/processes.
29+
30+ ** Custom ` asyncio ` Event Loop**
31+
32+ The workflow implementation basically turns ` async def ` functions into workflows backed by a distributed, fault-tolerant
33+ event loop. This means task management, sleep, cancellation, etc have all been developed to seamlessly integrate with
34+ ` asyncio ` concepts.
1435
1536** ⚠️ UNDER DEVELOPMENT**
1637
1738The Python SDK is under development. There are no compatibility guarantees nor proper documentation pages at this time.
1839
1940Currently missing features:
2041
21- * Async activity support (in client or worker)
2242* Support for Windows arm, macOS arm (i.e. M1), Linux arm, and Linux x64 glibc < 2.31.
2343* Full documentation
2444
@@ -35,7 +55,7 @@ These steps can be followed to use with a virtual environment and `pip`:
3555 * Needed because older versions of ` pip ` may not pick the right wheel
3656* Install Temporal SDK - ` python -m pip install temporalio `
3757
38- The SDK is now ready for use.
58+ The SDK is now ready for use. To build from source, see "Building" near the end of this documentation.
3959
4060### Implementing a Workflow
4161
@@ -142,6 +162,15 @@ Some things to note about the above code:
142162* Clients can have many more options not shown here (e.g. data converters and interceptors)
143163* A string can be used instead of the method reference to call a workflow by name (e.g. if defined in another language)
144164
165+ Clients also provide a shallow copy of their config for use in making slightly different clients backed by the same
166+ connection. For instance, given the ` client ` above, this is how to have a client in another namespace:
167+
168+ ``` python
169+ config = client.config()
170+ config[" namespace" ] = " my-other-namespace"
171+ other_ns_client = Client(** config)
172+ ```
173+
145174#### Data Conversion
146175
147176Data converters are used to convert raw Temporal payloads to/from actual Python types. A custom data converter of type
@@ -393,6 +422,16 @@ protect against cancellation. The following tasks, when cancelled, perform a Tem
393422When the workflow itself is requested to cancel, ` Task.cancel ` is called on the main workflow task. Therefore,
394423` asyncio.CancelledError ` can be caught in order to handle the cancel gracefully.
395424
425+ Workflows follow ` asyncio ` cancellation rules exactly which can cause confusion among Python developers. Cancelling a
426+ task doesn't always cancel the thing it created. For example, given
427+ ` task = asyncio.create_task(workflow.start_child_workflow(... ` , calling ` task.cancel ` does not cancel the child
428+ workflow, it only cancels the starting of it, which has no effect if it has already started. However, cancelling the
429+ result of ` handle = await workflow.start_child_workflow(... ` or
430+ ` task = asyncio.create_task(workflow.execute_child_workflow(... ` _ does_ cancel the child workflow.
431+
432+ Also, due to Temporal rules, a cancellation request is a state not an event. Therefore, repeated cancellation requests
433+ are not delivered, only the first. If the workflow chooses swallow a cancellation, it cannot be requested again.
434+
396435#### Workflow Utilities
397436
398437While running in a workflow, in addition to features documented elsewhere, the following items are available from the
@@ -405,11 +444,15 @@ While running in a workflow, in addition to features documented elsewhere, the f
405444
406445#### Exceptions
407446
408- TODO
447+ * Workflows can raise exceptions to fail the workflow
448+ * Using ` temporalio.exceptions.ApplicationError ` , exceptions can be marked as non-retryable or include details
409449
410450#### External Workflows
411451
412- TODO
452+ * ` workflow.get_external_workflow_handle() ` inside a workflow returns a handle to interact with another workflow
453+ * ` workflow.get_external_workflow_handle_for() ` can be used instead for a type safe handle
454+ * ` await handle.signal() ` can be called on the handle to signal the external workflow
455+ * ` await handle.cancel() ` can be called on the handle to send a cancel to the external workflow
413456
414457### Activities
415458
@@ -527,38 +570,137 @@ respect cancellation, the shutdown may never complete.
527570The Python SDK is built to work with Python 3.7 and newer. It is built using
528571[ SDK Core] ( https://github.com/temporalio/sdk-core/ ) which is written in Rust.
529572
530- ### Local development environment
573+ ### Building
574+
575+ #### Prepare
576+
577+ To build the SDK from source for use as a dependency, the following prerequisites are required:
578+
579+ * [ Python] ( https://www.python.org/ ) >= 3.7
580+ * [ Rust] ( https://www.rust-lang.org/ )
581+ * [ poetry] ( https://github.com/python-poetry/poetry ) (e.g. ` python -m pip install poetry ` )
582+ * [ poe] ( https://github.com/nat-n/poethepoet ) (e.g. ` python -m pip install poethepoet ` )
583+
584+ With the prerequisites installed, first clone the SDK repository recursively:
585+
586+ ``` bash
587+ git clone --recursive https://github.com/temporalio/sdk-python.git
588+ cd sdk-python
589+ ```
590+
591+ Use ` poetry ` to install the dependencies with ` --no-root ` to not install this package (because we still need to build
592+ it):
593+
594+ ``` bash
595+ poetry install --no-root
596+ ```
597+
598+ Now generate the protobuf code:
599+
600+ ``` bash
601+ poe gen-protos
602+ ```
603+
604+ #### Build
605+
606+ Now perform the release build:
607+
608+ ``` bash
609+ poetry build
610+ ```
611+
612+ This will take a while because Rust will compile the core project in release mode (see "Local SDK development
613+ environment" for the quicker approach to local development).
614+
615+ The compiled wheel doesn't have the exact right tags yet for use, so run this script to fix it:
616+
617+ ``` bash
618+ poe fix-wheel
619+ ```
620+
621+ The ` whl ` wheel file in ` dist/ ` is now ready to use.
622+
623+ #### Use
624+
625+ The wheel can now be installed into any virtual environment.
626+
627+ For example,
628+ [ create a virtual environment] ( https://packaging.python.org/en/latest/tutorials/installing-packages/#creating-virtual-environments )
629+ somewhere and then run the following inside the virtual environment:
630+
631+ ``` bash
632+ pip install /path/to/cloned/sdk-python/dist/* .whl
633+ ```
634+
635+ Create this Python file at ` example.py ` :
636+
637+ ``` python
638+ import asyncio
639+ from temporalio import workflow, activity
640+ from temporalio.client import Client
641+ from temporalio.worker import Worker
642+
643+ @workflow.defn
644+ class SayHello :
645+ @workflow.run
646+ async def run (self , name : str ) -> str :
647+ return f " Hello, { name} ! "
648+
649+ async def main ():
650+ client = await Client.connect(" http://localhost:7233" )
651+ async with Worker(client, task_queue = " my-task-queue" , workflows = [SayHello]):
652+ result = await client.execute_workflow(SayHello.run, " Temporal" ,
653+ id = " my-workflow-id" , task_queue = " my-task-queue" )
654+ print (f " Result: { result} " )
531655
532- - Install the system dependencies:
656+ if __name__ == " __main__" :
657+ asyncio.run(main())
658+ ```
533659
534- - Python >=3.7
535- - [ pipx] ( https://github.com/pypa/pipx#install-pipx ) (only needed for installing the two dependencies below)
536- - [ poetry] ( https://github.com/python-poetry/poetry ) ` pipx install poetry `
537- - [ poe] ( https://github.com/nat-n/poethepoet ) ` pipx install poethepoet `
660+ Assuming there is a [ local Temporal server] ( https://docs.temporal.io/docs/server/quick-install/ ) running, executing the
661+ file with ` python ` (or ` python3 ` if necessary) will give:
538662
539- - Use a local virtual env environment (helps IDEs and Windows):
663+ Result: Hello, Temporal!
540664
541- ``` bash
542- poetry config virtualenvs.in-project true
543- ```
665+ ### Local SDK development environment
544666
545- - Install the package dependencies (requires Rust):
667+ For local development, it is often quicker to use debug builds and a local virtual environment.
546668
547- ``` bash
548- poetry install
549- ```
669+ While not required, it often helps IDEs if we put the virtual environment ` .venv ` directory in the project itself. This
670+ can be configured system-wide via:
550671
551- - Build the project (requires Rust):
672+ ``` bash
673+ poetry config virtualenvs.in-project true
674+ ```
552675
553- ``` bash
554- poe build-develop
555- ```
676+ Now perform the same steps as the "Prepare" section above by installing the prerequisites, cloning the project,
677+ installing dependencies, and generating the protobuf code:
556678
557- - Run the tests (requires Go):
679+ ``` bash
680+ git clone --recursive https://github.com/temporalio/sdk-python.git
681+ cd sdk-python
682+ poetry install --no-root
683+ poe gen-protos
684+ ```
558685
559- ``` bash
560- poe test
561- ```
686+ Now compile the Rust extension in develop mode which is quicker than release mode:
687+
688+ ``` bash
689+ poe build-develop
690+ ```
691+
692+ That step can be repeated for any Rust changes made.
693+
694+ The environment is now ready to develop in.
695+
696+ #### Testing
697+
698+ Tests currently require [ Go] ( https://go.dev/ ) to be installed since they use an embedded Temporal server as a library.
699+ With ` Go ` installed, run the following to execute tests:
700+
701+ ``` bash
702+ poe test
703+ ```
562704
563705### Style
564706
0 commit comments