From ce6db6d948050ac7d87f3aa9e70797aa5cda6070 Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Wed, 3 Sep 2025 15:51:03 -0400 Subject: [PATCH 1/5] Stop making assertions on relative event timings --- ...test_workflow_caller_cancellation_types.py | 88 ++++++++----------- ...llation_types_when_cancel_handler_fails.py | 45 +++++----- 2 files changed, 63 insertions(+), 70 deletions(-) diff --git a/tests/nexus/test_workflow_caller_cancellation_types.py b/tests/nexus/test_workflow_caller_cancellation_types.py index 2d44e6416..0590bb2a6 100644 --- a/tests/nexus/test_workflow_caller_cancellation_types.py +++ b/tests/nexus/test_workflow_caller_cancellation_types.py @@ -11,7 +11,6 @@ import temporalio.nexus._operation_handlers from temporalio import exceptions, nexus, workflow from temporalio.api.enums.v1 import EventType -from temporalio.api.history.v1 import HistoryEvent from temporalio.client import ( WithStartWorkflowOperation, WorkflowExecutionStatus, @@ -311,10 +310,11 @@ async def check_behavior_for_abandon( await caller_wf.signal(CallerWorkflow.release) await caller_wf.result() await assert_event_subsequence( + caller_wf, [ - (caller_wf, EventType.EVENT_TYPE_WORKFLOW_EXECUTION_STARTED), - (caller_wf, EventType.EVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED), - ] + EventType.EVENT_TYPE_WORKFLOW_EXECUTION_STARTED, + EventType.EVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED, + ], ) assert not await has_event( caller_wf, @@ -347,16 +347,17 @@ async def check_behavior_for_try_cancel( handler_status = (await handler_wf.describe()).status assert handler_status == WorkflowExecutionStatus.CANCELED await assert_event_subsequence( + caller_wf, [ - (caller_wf, EventType.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUESTED), - (caller_wf, EventType.EVENT_TYPE_NEXUS_OPERATION_CANCELED), - ] + EventType.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUESTED, + EventType.EVENT_TYPE_NEXUS_OPERATION_CANCELED, + ], ) op_cancel_requested_event = await get_event_time( caller_wf, EventType.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUESTED, ) - assert result.caller_op_future_resolved < op_cancel_requested_event + assert result.caller_op_future_resolved <= op_cancel_requested_event async def check_behavior_for_wait_cancellation_requested( @@ -382,11 +383,12 @@ async def check_behavior_for_wait_cancellation_requested( handler_status = (await handler_wf.describe()).status assert handler_status == WorkflowExecutionStatus.CANCELED await assert_event_subsequence( + caller_wf, [ - (caller_wf, EventType.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUESTED), - (caller_wf, EventType.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_COMPLETED), - (caller_wf, EventType.EVENT_TYPE_NEXUS_OPERATION_CANCELED), - ] + EventType.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUESTED, + EventType.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_COMPLETED, + EventType.EVENT_TYPE_NEXUS_OPERATION_CANCELED, + ], ) op_cancel_request_completed = await get_event_time( caller_wf, @@ -396,7 +398,7 @@ async def check_behavior_for_wait_cancellation_requested( handler_wf, EventType.EVENT_TYPE_WORKFLOW_EXECUTION_CANCELED, ) - assert op_cancel_request_completed < result.caller_op_future_resolved < op_canceled + assert op_cancel_request_completed <= result.caller_op_future_resolved < op_canceled async def check_behavior_for_wait_cancellation_completed( @@ -421,23 +423,22 @@ async def check_behavior_for_wait_cancellation_completed( result = await caller_wf.result() await assert_event_subsequence( + caller_wf, [ - (caller_wf, EventType.EVENT_TYPE_WORKFLOW_EXECUTION_STARTED), - (caller_wf, EventType.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUESTED), - ( - handler_wf, - EventType.EVENT_TYPE_WORKFLOW_EXECUTION_CANCEL_REQUESTED, - ), - (handler_wf, EventType.EVENT_TYPE_WORKFLOW_EXECUTION_CANCELED), - (caller_wf, EventType.EVENT_TYPE_NEXUS_OPERATION_CANCELED), - (caller_wf, EventType.EVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED), - ] + EventType.EVENT_TYPE_WORKFLOW_EXECUTION_STARTED, + EventType.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUESTED, + EventType.EVENT_TYPE_NEXUS_OPERATION_CANCELED, + EventType.EVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED, + ], ) handler_wf_canceled_event = await get_event_time( handler_wf, EventType.EVENT_TYPE_WORKFLOW_EXECUTION_CANCELED, ) - assert handler_wf_canceled_event < result.caller_op_future_resolved + assert handler_wf_canceled_event <= result.caller_op_future_resolved, ( + "expected caller op future resolved after handler workflow canceled, but got " + f"{result.caller_op_future_resolved} before {handler_wf_canceled_event}" + ) async def has_event(wf_handle: WorkflowHandle, event_type: EventType.ValueType): @@ -459,46 +460,35 @@ async def get_event_time( async def assert_event_subsequence( - expected_events: list[tuple[WorkflowHandle, EventType.ValueType]], + wf_handle: WorkflowHandle, + expected_events: list[EventType.ValueType], ) -> None: """ - Given a sequence of (WorkflowHandle, EventType) pairs, assert that the sorted sequence of events - from both workflows contains that subsequence. + Given a workflow handle and a sequence of event types, assert that the workflow's history + contains that subsequence of events in the order specified. """ - - def _event_time( - item: tuple[WorkflowHandle, HistoryEvent], - ) -> datetime: - return item[1].event_time.ToDatetime() - all_events = [] - handles = {h for h, _ in expected_events} - for h in handles: - async for e in h.fetch_history_events(): - all_events.append((h, e)) - _all_events = iter(sorted(all_events, key=_event_time)) + async for e in wf_handle.fetch_history_events(): + all_events.append(e) + + _all_events = iter(all_events) _expected_events = iter(expected_events) - previous_expected_handle, previous_expected_event_type_name = None, None - for expected_handle, expected_event_type in _expected_events: + previous_expected_event_type_name = None + for expected_event_type in _expected_events: expected_event_type_name = EventType.Name(expected_event_type).removeprefix( "EVENT_TYPE_" ) has_expected = next( - ( - (h, e) - for h, e in _all_events - if h == expected_handle and e.event_type == expected_event_type - ), + (e for e in _all_events if e.event_type == expected_event_type), None, ) if not has_expected: - if previous_expected_handle is not None: - prefix = f"After {previous_expected_event_type_name} in {previous_expected_handle.id}, " + if previous_expected_event_type_name is not None: + prefix = f"After {previous_expected_event_type_name}, " else: prefix = "" pytest.fail( - f"{prefix}expected {expected_event_type_name} in {expected_handle.id}" + f"{prefix}expected {expected_event_type_name} in workflow {wf_handle.id}" ) previous_expected_event_type_name = expected_event_type_name - previous_expected_handle = expected_handle diff --git a/tests/nexus/test_workflow_caller_cancellation_types_when_cancel_handler_fails.py b/tests/nexus/test_workflow_caller_cancellation_types_when_cancel_handler_fails.py index 33b167245..12f99a714 100644 --- a/tests/nexus/test_workflow_caller_cancellation_types_when_cancel_handler_fails.py +++ b/tests/nexus/test_workflow_caller_cancellation_types_when_cancel_handler_fails.py @@ -279,10 +279,11 @@ async def check_behavior_for_abandon( assert result.error_cause_type == "CancelledError" await assert_event_subsequence( + caller_wf, [ - (caller_wf, EventType.EVENT_TYPE_WORKFLOW_EXECUTION_STARTED), - (caller_wf, EventType.EVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED), - ] + EventType.EVENT_TYPE_WORKFLOW_EXECUTION_STARTED, + EventType.EVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED, + ], ) assert not await has_event( caller_wf, @@ -301,11 +302,12 @@ async def check_behavior_for_try_cancel( assert result.error_cause_type == "CancelledError" await assert_event_subsequence( + caller_wf, [ - (caller_wf, EventType.EVENT_TYPE_WORKFLOW_EXECUTION_STARTED), - (caller_wf, EventType.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUESTED), - (caller_wf, EventType.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_FAILED), - ] + EventType.EVENT_TYPE_WORKFLOW_EXECUTION_STARTED, + EventType.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUESTED, + EventType.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_FAILED, + ], ) op_cancel_requested_event = await get_event_time( caller_wf, @@ -317,8 +319,8 @@ async def check_behavior_for_try_cancel( ) assert ( result.caller_op_future_resolved - < op_cancel_requested_event - < op_cancel_request_failed_event + <= op_cancel_requested_event + <= op_cancel_request_failed_event ) @@ -333,12 +335,13 @@ async def check_behavior_for_wait_cancellation_requested( await handler_wf.signal(HandlerWorkflow.set_caller_op_future_resolved) await handler_wf.result() await assert_event_subsequence( + caller_wf, [ - (caller_wf, EventType.EVENT_TYPE_WORKFLOW_EXECUTION_STARTED), - (caller_wf, EventType.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUESTED), - (caller_wf, EventType.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_FAILED), - (caller_wf, EventType.EVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED), - ] + EventType.EVENT_TYPE_WORKFLOW_EXECUTION_STARTED, + EventType.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUESTED, + EventType.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_FAILED, + EventType.EVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED, + ], ) op_cancel_request_failed = await get_event_time( caller_wf, @@ -350,8 +353,8 @@ async def check_behavior_for_wait_cancellation_requested( ) assert ( op_cancel_request_failed - < result.caller_op_future_resolved - < handler_wf_completed + <= result.caller_op_future_resolved + <= handler_wf_completed ) @@ -369,14 +372,14 @@ async def check_behavior_for_wait_cancellation_completed( # (caller_wf, EventType.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUEST_FAILED) # (handler_wf, EventType.EVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED) await assert_event_subsequence( + caller_wf, [ - (caller_wf, EventType.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUESTED), - (handler_wf, EventType.EVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED), - (caller_wf, EventType.EVENT_TYPE_NEXUS_OPERATION_COMPLETED), - ] + EventType.EVENT_TYPE_NEXUS_OPERATION_CANCEL_REQUESTED, + EventType.EVENT_TYPE_NEXUS_OPERATION_COMPLETED, + ], ) handler_wf_completed = await get_event_time( handler_wf, EventType.EVENT_TYPE_WORKFLOW_EXECUTION_COMPLETED, ) - assert handler_wf_completed < result.caller_op_future_resolved + assert handler_wf_completed <= result.caller_op_future_resolved From f951358a53eefa4e7a3884283418d240a1620b30 Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Wed, 27 Aug 2025 09:52:24 -0400 Subject: [PATCH 2/5] Restrict CI and add debugging output --- .github/workflows/ci.yml | 61 ---------------------------------------- pyproject.toml | 2 +- 2 files changed, 1 insertion(+), 62 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a9637f8ad..3c822ddd8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,23 +76,6 @@ jobs: - run: mkdir junit-xml - run: poe test ${{matrix.pytestExtraArgs}} -s --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}.xml timeout-minutes: 15 - # Time skipping doesn't yet support ARM - - if: ${{ !endsWith(matrix.os, '-arm') }} - run: poe test ${{matrix.pytestExtraArgs}} -s --workflow-environment time-skipping --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}--time-skipping.xml - timeout-minutes: 10 - # Check cloud if proper target and not on fork - - if: ${{ matrix.cloudTestTarget && (github.event.pull_request.head.repo.full_name == '' || github.event.pull_request.head.repo.full_name == 'temporalio/sdk-python') }} - run: poe test ${{matrix.pytestExtraArgs}} -s -k test_cloud_client --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}--cloud.xml - timeout-minutes: 10 - env: - TEMPORAL_CLIENT_CLOUD_API_KEY: ${{ secrets.TEMPORAL_CLIENT_CLOUD_API_KEY }} - TEMPORAL_CLIENT_CLOUD_API_VERSION: 2024-05-13-00 - TEMPORAL_CLIENT_CLOUD_NAMESPACE: sdk-ci.a2dd6 - - if: ${{ matrix.openaiTestTarget && (github.event.pull_request.head.repo.full_name == '' || github.event.pull_request.head.repo.full_name == 'temporalio/sdk-python') }} - run: poe test tests/contrib/openai_agents/test_openai.py ${{matrix.pytestExtraArgs}} -s --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}--openai.xml - timeout-minutes: 10 - env: - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - name: "Upload junit-xml artifacts" uses: actions/upload-artifact@v4 if: always() @@ -101,43 +84,7 @@ jobs: path: junit-xml retention-days: 14 - # Confirm protos are already generated properly with older protobuf - # library and run test with that older version. We must downgrade protobuf - # so we can generate 3.x and 4.x compatible API. We have to use older - # Python to run this check because the grpcio-tools version we use - # is <= 3.10. - - name: Check generated protos and test protobuf 3.x - if: ${{ matrix.protoCheckTarget }} - env: - TEMPORAL_TEST_PROTO3: 1 - run: | - uv add --python 3.9 "protobuf<4" - uv sync --all-extras - poe build-develop - poe gen-protos - poe format - [[ -z $(git status --porcelain temporalio) ]] || (git diff temporalio; echo "Protos changed"; exit 1) - poe test -s - timeout-minutes: 10 - - # Do docs stuff (only on one host) - - name: Build API docs - if: ${{ matrix.docsTarget }} - run: poe gen-docs - - name: Deploy prod API docs - if: ${{ github.ref == 'refs/heads/main' && matrix.docsTarget }} - env: - VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} - VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} - run: npx vercel deploy build/apidocs -t ${{ secrets.VERCEL_TOKEN }} --prod --yes - # Confirm README ToC is generated properly - - uses: actions/setup-node@v4 - - name: Check generated README ToC - if: ${{ matrix.docsTarget }} - run: | - npx doctoc README.md - [[ -z $(git status --porcelain README.md) ]] || (git diff README.md; echo "README changed"; exit 1) test-latest-deps: timeout-minutes: 30 runs-on: ubuntu-latest @@ -173,11 +120,3 @@ jobs: name: junit-xml--${{github.run_id}}--${{github.run_attempt}}--latest-deps--time-skipping path: junit-xml retention-days: 14 - - # Runs the sdk features repo tests with this repo's current SDK code - features-tests: - uses: temporalio/features/.github/workflows/python.yaml@main - with: - python-repo-path: ${{github.event.pull_request.head.repo.full_name}} - version: ${{github.event.pull_request.head.ref}} - version-is-repo-ref: true diff --git a/pyproject.toml b/pyproject.toml index 3e2321fd0..656f3e9ce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -81,7 +81,7 @@ lint-types = [ { cmd = "uv run mypy --namespace-packages --check-untyped-defs ."}, ] run-bench = "uv run python scripts/run_bench.py" -test = "uv run pytest" +test = "uv run pytest -s 'tests/nexus/test_workflow_caller_cancellation_types.py::test_cancellation_type' 'tests/nexus/test_workflow_caller_cancellation_types_when_cancel_handler_fails.py::test_cancellation_type'" [tool.pytest.ini_options] From 1163b778b8872dd6c42491876ede6ae93a2da80a Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Wed, 27 Aug 2025 10:31:39 -0400 Subject: [PATCH 3/5] Run multiple times --- .github/workflows/ci.yml | 56 +++++++++++++--------------------------- 1 file changed, 18 insertions(+), 38 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3c822ddd8..d548e5fb6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,9 +71,26 @@ jobs: - run: uv sync --all-extras - run: poe bridge-lint if: ${{ matrix.clippyLinter }} - - run: poe lint - run: poe build-develop - run: mkdir junit-xml + - run: poe test ${{matrix.pytestExtraArgs}} -s --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}.xml + timeout-minutes: 15 + - run: poe test ${{matrix.pytestExtraArgs}} -s --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}.xml + timeout-minutes: 15 + - run: poe test ${{matrix.pytestExtraArgs}} -s --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}.xml + timeout-minutes: 15 + - run: poe test ${{matrix.pytestExtraArgs}} -s --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}.xml + timeout-minutes: 15 + - run: poe test ${{matrix.pytestExtraArgs}} -s --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}.xml + timeout-minutes: 15 + - run: poe test ${{matrix.pytestExtraArgs}} -s --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}.xml + timeout-minutes: 15 + - run: poe test ${{matrix.pytestExtraArgs}} -s --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}.xml + timeout-minutes: 15 + - run: poe test ${{matrix.pytestExtraArgs}} -s --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}.xml + timeout-minutes: 15 + - run: poe test ${{matrix.pytestExtraArgs}} -s --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}.xml + timeout-minutes: 15 - run: poe test ${{matrix.pytestExtraArgs}} -s --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}.xml timeout-minutes: 15 - name: "Upload junit-xml artifacts" @@ -83,40 +100,3 @@ jobs: name: junit-xml--${{github.run_id}}--${{github.run_attempt}}--${{ matrix.python }}--${{ matrix.os }} path: junit-xml retention-days: 14 - - - test-latest-deps: - timeout-minutes: 30 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - with: - workspaces: temporalio/bridge -> target - - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - uses: arduino/setup-protoc@v3 - with: - # TODO(cretz): Can upgrade proto when https://github.com/arduino/setup-protoc/issues/99 fixed - version: "23.x" - repo-token: ${{ secrets.GITHUB_TOKEN }} - - uses: astral-sh/setup-uv@v5 - - run: uv tool install poethepoet - - run: uv lock --upgrade - - run: uv sync --all-extras - - run: poe lint - - run: poe build-develop - - run: mkdir junit-xml - - run: poe test -s --junit-xml=junit-xml/latest-deps.xml - timeout-minutes: 10 - - name: "Upload junit-xml artifacts" - uses: actions/upload-artifact@v4 - if: always() - with: - name: junit-xml--${{github.run_id}}--${{github.run_attempt}}--latest-deps--time-skipping - path: junit-xml - retention-days: 14 From 47774c63e614e19e67ece04759b08984373783ff Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Wed, 3 Sep 2025 16:01:43 -0400 Subject: [PATCH 4/5] Revert "Run multiple times" This reverts commit ff0881b159af13d1ee4a83f3a60335ab29e392a3. --- .github/workflows/ci.yml | 56 +++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d548e5fb6..3c822ddd8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,26 +71,9 @@ jobs: - run: uv sync --all-extras - run: poe bridge-lint if: ${{ matrix.clippyLinter }} + - run: poe lint - run: poe build-develop - run: mkdir junit-xml - - run: poe test ${{matrix.pytestExtraArgs}} -s --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}.xml - timeout-minutes: 15 - - run: poe test ${{matrix.pytestExtraArgs}} -s --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}.xml - timeout-minutes: 15 - - run: poe test ${{matrix.pytestExtraArgs}} -s --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}.xml - timeout-minutes: 15 - - run: poe test ${{matrix.pytestExtraArgs}} -s --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}.xml - timeout-minutes: 15 - - run: poe test ${{matrix.pytestExtraArgs}} -s --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}.xml - timeout-minutes: 15 - - run: poe test ${{matrix.pytestExtraArgs}} -s --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}.xml - timeout-minutes: 15 - - run: poe test ${{matrix.pytestExtraArgs}} -s --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}.xml - timeout-minutes: 15 - - run: poe test ${{matrix.pytestExtraArgs}} -s --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}.xml - timeout-minutes: 15 - - run: poe test ${{matrix.pytestExtraArgs}} -s --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}.xml - timeout-minutes: 15 - run: poe test ${{matrix.pytestExtraArgs}} -s --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}.xml timeout-minutes: 15 - name: "Upload junit-xml artifacts" @@ -100,3 +83,40 @@ jobs: name: junit-xml--${{github.run_id}}--${{github.run_attempt}}--${{ matrix.python }}--${{ matrix.os }} path: junit-xml retention-days: 14 + + + test-latest-deps: + timeout-minutes: 30 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + with: + workspaces: temporalio/bridge -> target + - uses: actions/setup-python@v5 + with: + python-version: "3.13" + - uses: arduino/setup-protoc@v3 + with: + # TODO(cretz): Can upgrade proto when https://github.com/arduino/setup-protoc/issues/99 fixed + version: "23.x" + repo-token: ${{ secrets.GITHUB_TOKEN }} + - uses: astral-sh/setup-uv@v5 + - run: uv tool install poethepoet + - run: uv lock --upgrade + - run: uv sync --all-extras + - run: poe lint + - run: poe build-develop + - run: mkdir junit-xml + - run: poe test -s --junit-xml=junit-xml/latest-deps.xml + timeout-minutes: 10 + - name: "Upload junit-xml artifacts" + uses: actions/upload-artifact@v4 + if: always() + with: + name: junit-xml--${{github.run_id}}--${{github.run_attempt}}--latest-deps--time-skipping + path: junit-xml + retention-days: 14 From 0fc0ab620b557a52f0e0a1743752d7ecbe3947a7 Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Wed, 3 Sep 2025 16:01:46 -0400 Subject: [PATCH 5/5] Revert "Restrict CI and add debugging output" This reverts commit 4150d90d755e78768a0ecb5a79f09d9ea1c70a6c. --- .github/workflows/ci.yml | 61 ++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3c822ddd8..a9637f8ad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,6 +76,23 @@ jobs: - run: mkdir junit-xml - run: poe test ${{matrix.pytestExtraArgs}} -s --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}.xml timeout-minutes: 15 + # Time skipping doesn't yet support ARM + - if: ${{ !endsWith(matrix.os, '-arm') }} + run: poe test ${{matrix.pytestExtraArgs}} -s --workflow-environment time-skipping --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}--time-skipping.xml + timeout-minutes: 10 + # Check cloud if proper target and not on fork + - if: ${{ matrix.cloudTestTarget && (github.event.pull_request.head.repo.full_name == '' || github.event.pull_request.head.repo.full_name == 'temporalio/sdk-python') }} + run: poe test ${{matrix.pytestExtraArgs}} -s -k test_cloud_client --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}--cloud.xml + timeout-minutes: 10 + env: + TEMPORAL_CLIENT_CLOUD_API_KEY: ${{ secrets.TEMPORAL_CLIENT_CLOUD_API_KEY }} + TEMPORAL_CLIENT_CLOUD_API_VERSION: 2024-05-13-00 + TEMPORAL_CLIENT_CLOUD_NAMESPACE: sdk-ci.a2dd6 + - if: ${{ matrix.openaiTestTarget && (github.event.pull_request.head.repo.full_name == '' || github.event.pull_request.head.repo.full_name == 'temporalio/sdk-python') }} + run: poe test tests/contrib/openai_agents/test_openai.py ${{matrix.pytestExtraArgs}} -s --junit-xml=junit-xml/${{ matrix.python }}--${{ matrix.os }}--openai.xml + timeout-minutes: 10 + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - name: "Upload junit-xml artifacts" uses: actions/upload-artifact@v4 if: always() @@ -84,7 +101,43 @@ jobs: path: junit-xml retention-days: 14 + # Confirm protos are already generated properly with older protobuf + # library and run test with that older version. We must downgrade protobuf + # so we can generate 3.x and 4.x compatible API. We have to use older + # Python to run this check because the grpcio-tools version we use + # is <= 3.10. + - name: Check generated protos and test protobuf 3.x + if: ${{ matrix.protoCheckTarget }} + env: + TEMPORAL_TEST_PROTO3: 1 + run: | + uv add --python 3.9 "protobuf<4" + uv sync --all-extras + poe build-develop + poe gen-protos + poe format + [[ -z $(git status --porcelain temporalio) ]] || (git diff temporalio; echo "Protos changed"; exit 1) + poe test -s + timeout-minutes: 10 + + # Do docs stuff (only on one host) + - name: Build API docs + if: ${{ matrix.docsTarget }} + run: poe gen-docs + - name: Deploy prod API docs + if: ${{ github.ref == 'refs/heads/main' && matrix.docsTarget }} + env: + VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} + VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} + run: npx vercel deploy build/apidocs -t ${{ secrets.VERCEL_TOKEN }} --prod --yes + # Confirm README ToC is generated properly + - uses: actions/setup-node@v4 + - name: Check generated README ToC + if: ${{ matrix.docsTarget }} + run: | + npx doctoc README.md + [[ -z $(git status --porcelain README.md) ]] || (git diff README.md; echo "README changed"; exit 1) test-latest-deps: timeout-minutes: 30 runs-on: ubuntu-latest @@ -120,3 +173,11 @@ jobs: name: junit-xml--${{github.run_id}}--${{github.run_attempt}}--latest-deps--time-skipping path: junit-xml retention-days: 14 + + # Runs the sdk features repo tests with this repo's current SDK code + features-tests: + uses: temporalio/features/.github/workflows/python.yaml@main + with: + python-repo-path: ${{github.event.pull_request.head.repo.full_name}} + version: ${{github.event.pull_request.head.ref}} + version-is-repo-ref: true diff --git a/pyproject.toml b/pyproject.toml index 656f3e9ce..3e2321fd0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -81,7 +81,7 @@ lint-types = [ { cmd = "uv run mypy --namespace-packages --check-untyped-defs ."}, ] run-bench = "uv run python scripts/run_bench.py" -test = "uv run pytest -s 'tests/nexus/test_workflow_caller_cancellation_types.py::test_cancellation_type' 'tests/nexus/test_workflow_caller_cancellation_types_when_cancel_handler_fails.py::test_cancellation_type'" +test = "uv run pytest" [tool.pytest.ini_options]