I wrote a quick example to check this:
import asyncio
import logging
from temporalio import workflow
from temporalio.worker import Worker
from temporalio.testing import WorkflowEnvironment
@workflow.defn
class MyWorkflowParent:
@workflow.run
async def run(self) -> None:
workflow.logger.info("Running parent workflow")
await workflow.execute_child_workflow(
MyWorkflowChild.run,
id="my-child-workflow",
)
workflow.logger.info("Completing parent workflow")
@workflow.defn
class MyWorkflowChild:
def __init__(self) -> None:
self.got_signal = False
@workflow.run
async def run(self) -> None:
workflow.logger.info("Running child workflow, waiting on signal")
await workflow.wait_condition(lambda: self.got_signal)
workflow.logger.info("Completing child workflow")
@workflow.signal
def signal(self) -> None:
self.got_signal = True
async def main():
# Uncomment the line below to see logging
logging.basicConfig(level=logging.INFO)
logging.info("Starting local server")
async with await WorkflowEnvironment.start_local() as env:
logger = logging.getLogger("root")
logger.info("logging in main", extra={"foo": "in main"})
logging.info("Starting worker")
async with Worker(
env.client,
task_queue="my-task-queue",
workflows=[MyWorkflowParent, MyWorkflowChild],
):
logging.info("Starting parent")
handle = await env.client.start_workflow(
MyWorkflowParent.run,
id="my-parent-workflow",
task_queue="my-task-queue",
)
logging.info("Waiting 5 seconds then listing workflows")
await asyncio.sleep(5)
async for workflow in env.client.list_workflows():
logging.info("List returned workflow: %s" % workflow.id)
logging.info("Signalling child")
child_handle = env.client.get_workflow_handle_for(
MyWorkflowChild.run,
"my-child-workflow",
)
await child_handle.signal(MyWorkflowChild.signal)
logging.info("Waiting 5 seconds then confirming workflow complete")
await asyncio.sleep(5)
await handle.result()
logging.info("Parent workflow complete")
if __name__ == "__main__":
asyncio.run(main())
This logs the following output:
INFO:root:Starting local server
INFO:root:logging in main
INFO:root:Starting worker
INFO:root:Starting parent
INFO:root:Waiting 5 seconds then listing workflows
INFO:temporalio.workflow:Running parent workflow ({'attempt': 1, 'namespace': 'default', 'run_id': 'dd58c921-4e2e-4550-a178-6b516c9365f4', 'task_queue': 'my-task-queue', 'workflow_id': 'my-parent-workflow', 'workflow_type': 'MyWorkflowParent'})
INFO:temporalio.workflow:Running child workflow, waiting on signal ({'attempt': 1, 'namespace': 'default', 'run_id': '4b0297b1-e819-404c-bfd8-1581ba4849d6', 'task_queue': 'my-task-queue', 'workflow_id': 'my-child-workflow', 'workflow_type': 'MyWorkflowChild'})
INFO:root:List returned workflow: my-child-workflow
INFO:root:List returned workflow: my-parent-workflow
INFO:root:Signalling child
INFO:root:Waiting 5 seconds then confirming workflow complete
INFO:temporalio.workflow:Completing child workflow ({'attempt': 1, 'namespace': 'default', 'run_id': '4b0297b1-e819-404c-bfd8-1581ba4849d6', 'task_queue': 'my-task-queue', 'workflow_id': 'my-child-workflow', 'workflow_type': 'MyWorkflowChild'})
INFO:temporalio.workflow:Completing parent workflow ({'attempt': 1, 'namespace': 'default', 'run_id': 'dd58c921-4e2e-4550-a178-6b516c9365f4', 'task_queue': 'my-task-queue', 'workflow_id': 'my-parent-workflow', 'workflow_type': 'MyWorkflowParent'})
INFO:root:Parent workflow complete
INFO:temporalio.worker._worker:Beginning worker shutdown, will wait 0:00:00 before cancelling activities
Notice how the child is returned from list as expected