durable-workflow 2.1.1__tar.gz → 2.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. {durable_workflow-2.1.1/src/durable_workflow.egg-info → durable_workflow-2.1.2}/PKG-INFO +1 -1
  2. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/pyproject.toml +3 -3
  3. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow/worker.py +12 -3
  4. {durable_workflow-2.1.1 → durable_workflow-2.1.2/src/durable_workflow.egg-info}/PKG-INFO +1 -1
  5. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_worker.py +59 -0
  6. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/LICENSE +0 -0
  7. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/README.md +0 -0
  8. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/setup.cfg +0 -0
  9. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow/__init__.py +0 -0
  10. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow/_avro.py +0 -0
  11. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow/activity.py +0 -0
  12. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow/auth_composition.py +0 -0
  13. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow/client.py +0 -0
  14. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow/errors.py +0 -0
  15. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow/external_storage.py +0 -0
  16. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow/external_task_input.py +0 -0
  17. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow/external_task_result.py +0 -0
  18. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow/history_bundle_verify.py +0 -0
  19. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow/interceptors.py +0 -0
  20. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow/invocable.py +0 -0
  21. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow/metrics.py +0 -0
  22. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow/nexus.py +0 -0
  23. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow/py.typed +0 -0
  24. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow/python_conformance.py +0 -0
  25. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow/replay_conformance.py +0 -0
  26. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow/replay_verify.py +0 -0
  27. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow/retry_policy.py +0 -0
  28. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow/serializer.py +0 -0
  29. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow/sync.py +0 -0
  30. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow/testing.py +0 -0
  31. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow/workflow.py +0 -0
  32. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow/workflow_updates_conformance.py +0 -0
  33. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow.egg-info/SOURCES.txt +0 -0
  34. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow.egg-info/dependency_links.txt +0 -0
  35. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow.egg-info/entry_points.txt +0 -0
  36. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow.egg-info/requires.txt +0 -0
  37. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/src/durable_workflow.egg-info/top_level.txt +0 -0
  38. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_activity_context.py +0 -0
  39. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_auth_composition.py +0 -0
  40. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_avro_value_benchmark.py +0 -0
  41. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_avro_value_protocol.py +0 -0
  42. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_ci_checkout.py +0 -0
  43. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_ci_integration_endpoint.py +0 -0
  44. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_client.py +0 -0
  45. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_codec_regression_corpus.py +0 -0
  46. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_control_plane_parity_fixtures.py +0 -0
  47. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_docstring_cross_references.py +0 -0
  48. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_durable_selection.py +0 -0
  49. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_errors.py +0 -0
  50. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_external_storage.py +0 -0
  51. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_external_task_input.py +0 -0
  52. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_external_task_result.py +0 -0
  53. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_golden_history_replay.py +0 -0
  54. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_history_bundle_verify.py +0 -0
  55. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_history_event_contract.py +0 -0
  56. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_invocable.py +0 -0
  57. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_message_streams.py +0 -0
  58. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_metrics.py +0 -0
  59. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_order_processing_example.py +0 -0
  60. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_parallel_completion_order_replay.py +0 -0
  61. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_parallel_saga_authoring.py +0 -0
  62. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_public_boundary_scanner.py +0 -0
  63. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_pypi_project_surface.py +0 -0
  64. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_python_conformance.py +0 -0
  65. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_queries.py +0 -0
  66. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_readme_quickstart.py +0 -0
  67. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_redrive_replay.py +0 -0
  68. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_release_metadata.py +0 -0
  69. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_replay.py +0 -0
  70. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_replay_conformance.py +0 -0
  71. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_replay_regression_corpus.py +0 -0
  72. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_replay_verify.py +0 -0
  73. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_retry_policy.py +0 -0
  74. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_runtime_external_payload_transport.py +0 -0
  75. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_schedules.py +0 -0
  76. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_serializer.py +0 -0
  77. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_signals.py +0 -0
  78. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_sleep.py +0 -0
  79. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_standalone_activity_client.py +0 -0
  80. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_storage_admission.py +0 -0
  81. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_sync.py +0 -0
  82. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_testing_harness.py +0 -0
  83. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_update_signal_condition_replay.py +0 -0
  84. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_updates.py +0 -0
  85. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_wait_condition.py +0 -0
  86. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_workflow_result_timeout.py +0 -0
  87. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_workflow_streams.py +0 -0
  88. {durable_workflow-2.1.1 → durable_workflow-2.1.2}/tests/test_workflow_updates_conformance.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: durable-workflow
3
- Version: 2.1.1
3
+ Version: 2.1.2
4
4
  Summary: Python client and worker SDK for Durable Workflow Cloud and self-hosted Server
5
5
  Author: Durable Workflow Contributors
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "durable-workflow"
7
- version = "2.1.1"
7
+ version = "2.1.2"
8
8
  description = "Python client and worker SDK for Durable Workflow Cloud and self-hosted Server"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -71,8 +71,8 @@ durable-workflow-replay-conformance = "durable_workflow.replay_conformance:main"
71
71
  durable-workflow-workflow-updates-conformance = "durable_workflow.workflow_updates_conformance:main"
72
72
 
73
73
  [tool.durable-workflow]
74
- product-train = "2.1.1"
75
- registry-version = "2.1.1"
74
+ product-train = "2.1.2"
75
+ registry-version = "2.1.2"
76
76
  supported-server-versions = "2.4.0"
77
77
  worker-protocol-version = "1.19"
78
78
  control-plane-version = "2"
@@ -162,6 +162,15 @@ def _is_storage_admission_error(error: BaseException) -> bool:
162
162
  )
163
163
 
164
164
 
165
+ def _poll_error_summary(error: BaseException) -> str:
166
+ if not isinstance(error, ServerError):
167
+ return str(error)
168
+ reason = error.reason()
169
+ if not isinstance(reason, str) or not reason:
170
+ return f"server returned {error.status}"
171
+ return f"server returned {error.status} reason={reason[:64]!r}"
172
+
173
+
165
174
  def _command_payload_codec(codec: object) -> str:
166
175
  if codec is None:
167
176
  return serializer.AVRO_CODEC
@@ -2184,7 +2193,7 @@ class Worker:
2184
2193
  if _is_storage_admission_error(e):
2185
2194
  raise
2186
2195
  self._record_poll_metrics("workflow", "error", time.perf_counter() - poll_start)
2187
- log.warning("workflow poll error: %s", e)
2196
+ log.warning("workflow poll error: %s", _poll_error_summary(e))
2188
2197
  await asyncio.sleep(1.0)
2189
2198
  continue
2190
2199
  if task is None:
@@ -2282,7 +2291,7 @@ class Worker:
2282
2291
  if _is_storage_admission_error(e):
2283
2292
  raise
2284
2293
  self._record_poll_metrics("activity", "error", time.perf_counter() - poll_start)
2285
- log.warning("activity poll error: %s", e)
2294
+ log.warning("activity poll error: %s", _poll_error_summary(e))
2286
2295
  await asyncio.sleep(1.0)
2287
2296
  continue
2288
2297
  if task is None:
@@ -2333,7 +2342,7 @@ class Worker:
2333
2342
  if _is_storage_admission_error(e):
2334
2343
  raise
2335
2344
  self._record_poll_metrics("query", "error", time.perf_counter() - poll_start)
2336
- log.warning("query poll error: %s", e)
2345
+ log.warning("query poll error: %s", _poll_error_summary(e))
2337
2346
  await asyncio.sleep(1.0)
2338
2347
  continue
2339
2348
  if task is None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: durable-workflow
3
- Version: 2.1.1
3
+ Version: 2.1.2
4
4
  Summary: Python client and worker SDK for Durable Workflow Cloud and self-hosted Server
5
5
  Author: Durable Workflow Contributors
6
6
  License-Expression: MIT
@@ -820,6 +820,65 @@ class TestWorkerRegistration:
820
820
  assert query_called.is_set()
821
821
  assert query_kwargs["build_id"] == "release-2026.04.22-a1"
822
822
 
823
+ @pytest.mark.parametrize(
824
+ ("poll_method", "loop_method", "task_kind"),
825
+ [
826
+ ("poll_workflow_task", "_poll_workflow_tasks", "workflow"),
827
+ ("poll_activity_task", "_poll_activity_tasks", "activity"),
828
+ ("poll_query_task", "_poll_query_tasks", "query"),
829
+ ],
830
+ )
831
+ @pytest.mark.asyncio
832
+ async def test_poll_warning_omits_large_server_response(
833
+ self,
834
+ mock_client: AsyncMock,
835
+ monkeypatch: pytest.MonkeyPatch,
836
+ caplog: pytest.LogCaptureFixture,
837
+ poll_method: str,
838
+ loop_method: str,
839
+ task_kind: str,
840
+ ) -> None:
841
+ worker = Worker(
842
+ mock_client,
843
+ task_queue="q1",
844
+ workflows=[TestWorkflow],
845
+ activities=[echo_activity],
846
+ )
847
+ getattr(mock_client, poll_method).side_effect = ServerError(
848
+ 429,
849
+ {
850
+ "reason": "long_poll_capacity_exhausted",
851
+ "server_capabilities": {"large": "x" * 10_000},
852
+ },
853
+ )
854
+
855
+ async def stop_after_backoff(seconds: float) -> None:
856
+ assert seconds == 1.0
857
+ worker._stop.set()
858
+
859
+ monkeypatch.setattr(worker_module.asyncio, "sleep", stop_after_backoff)
860
+ with caplog.at_level(logging.WARNING, logger="durable_workflow.worker"):
861
+ await getattr(worker, loop_method)()
862
+
863
+ warnings = [
864
+ record.getMessage()
865
+ for record in caplog.records
866
+ if record.name == "durable_workflow.worker"
867
+ ]
868
+ assert warnings == [
869
+ f"{task_kind} poll error: server returned 429 "
870
+ "reason='long_poll_capacity_exhausted'"
871
+ ]
872
+ assert "server_capabilities" not in caplog.text
873
+
874
+ def test_poll_error_summary_bounds_reason(self) -> None:
875
+ summary = worker_module._poll_error_summary(
876
+ ServerError(429, {"reason": "x" * 10_000})
877
+ )
878
+ assert summary == f"server returned 429 reason={'x' * 64!r}"
879
+ assert worker_module._poll_error_summary(ServerError(503, {})) == "server returned 503"
880
+ assert worker_module._poll_error_summary(ValueError("transport failed")) == "transport failed"
881
+
823
882
  @pytest.mark.asyncio
824
883
  async def test_register_omits_build_id_when_not_configured(
825
884
  self, mock_client: AsyncMock