durable-workflow 2.11.1__tar.gz → 2.11.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.
- {durable_workflow-2.11.1/src/durable_workflow.egg-info → durable_workflow-2.11.2}/PKG-INFO +1 -1
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/pyproject.toml +3 -3
- durable_workflow-2.11.2/src/durable_workflow/_poll_transport.py +55 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/client.py +28 -4
- {durable_workflow-2.11.1 → durable_workflow-2.11.2/src/durable_workflow.egg-info}/PKG-INFO +1 -1
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow.egg-info/SOURCES.txt +2 -0
- durable_workflow-2.11.2/tests/test_poll_transport.py +255 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/LICENSE +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/README.md +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/setup.cfg +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/__init__.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/_activity_lease.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/_activity_process.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/_avro.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/_cancellation_scope.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/_cancellation_scope_history.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/_cooperative_cancellation.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/_failure_reporting.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/_metric_routes.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/_prepared_local_activity.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/_query_task_authority.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/_sticky_workflow_cache.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/activity.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/auth_composition.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/cancellation.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/errors.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/external_storage.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/external_task_input.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/external_task_result.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/history_bundle_verify.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/interceptors.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/invocable.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/metrics.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/nexus.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/py.typed +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/python_conformance.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/replay_conformance.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/replay_verify.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/retry_policy.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/serializer.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/sync.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/telemetry.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/telemetry_carrier.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/testing.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/worker.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/worker_session.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/workflow.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/workflow_updates_conformance.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow.egg-info/dependency_links.txt +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow.egg-info/entry_points.txt +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow.egg-info/requires.txt +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow.egg-info/top_level.txt +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_activity_cancellation_policies.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_activity_context.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_activity_heartbeat_authority.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_activity_lease_authority.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_activity_lease_renewal.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_activity_lease_worker.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_activity_process.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_auth_composition.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_avro_value_benchmark.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_avro_value_protocol.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_cancellation_context.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_cancellation_remaining_time.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_cancellation_scope_admission.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_cancellation_scope_authoring.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_cancellation_scope_boundary.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_cancellation_scope_opening.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_child_workflow_policies.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_ci_checkout.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_ci_integration_endpoint.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_client.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_client_telemetry.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_client_timeouts.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_codec_regression_corpus.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_committed_cancellation_scope_descendants.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_committed_cancellation_scope_groups.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_committed_cancellation_scope_history.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_committed_cancellation_scope_replay.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_condition_replay_drift.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_control_plane_parity_fixtures.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_cooperative_cancellation.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_cooperative_cancellation_client.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_cooperative_cancellation_worker.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_cooperative_remote_worker.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_docstring_cross_references.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_durable_selection.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_errors.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_external_storage.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_external_task_input.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_external_task_result.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_failure_reporting.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_golden_history_replay.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_history_bundle_schema2.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_history_bundle_verify.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_history_event_contract.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_invocable.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_local_failure_reporting.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_message_streams.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_metric_routes.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_metrics.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_nexus_durable_wait.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_nexus_namespace.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_nexus_wire.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_order_processing_example.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_parallel_completion_order_replay.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_parallel_saga_authoring.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_patch_history.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_pending_operation_replay.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_poll_failure_backoff.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_prepared_local_activity_client.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_prepared_local_activity_groups.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_prepared_local_activity_policies.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_prepared_local_activity_worker.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_public_boundary_scanner.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_pypi_project_surface.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_python_conformance.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_queries.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_query_task_authority.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_query_timeout_retry.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_readme_quickstart.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_redrive_replay.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_registration_ambiguity.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_release_metadata.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_replay.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_replay_clock.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_replay_conformance.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_replay_regression_corpus.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_replay_verify.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_retry_policy.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_run_cancellation_scope_replay.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_runtime_external_payload_transport.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_schedules.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_scoped_run_cancellation_context.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_sdk_metrics_contract.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_serializer.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_signals.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_sleep.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_standalone_activity_client.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_sticky_execution.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_storage_admission.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_sync.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_sync_activities.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_telemetry.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_telemetry_carrier.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_testing_failures.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_testing_harness.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_testing_nexus.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_threaded_heartbeat_example.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_tls_trust.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_transport_diagnostics.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_update_signal_condition_replay.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_updates.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_wait_condition.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_worker.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_worker_deregistration.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_worker_observation.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_worker_session.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_worker_telemetry_wire.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_workflow_entry_arguments.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_workflow_entrypoint_validation.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_workflow_handler_validation.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_workflow_identity.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_workflow_result_timeout.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_workflow_streams.py +0 -0
- {durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_workflow_updates_conformance.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "durable-workflow"
|
|
7
|
-
version = "2.11.
|
|
7
|
+
version = "2.11.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"
|
|
@@ -76,8 +76,8 @@ durable-workflow-replay-conformance = "durable_workflow.replay_conformance:main"
|
|
|
76
76
|
durable-workflow-workflow-updates-conformance = "durable_workflow.workflow_updates_conformance:main"
|
|
77
77
|
|
|
78
78
|
[tool.durable-workflow]
|
|
79
|
-
product-train = "2.11.
|
|
80
|
-
registry-version = "2.11.
|
|
79
|
+
product-train = "2.11.2"
|
|
80
|
+
registry-version = "2.11.2"
|
|
81
81
|
supported-server-versions = "2.8.0"
|
|
82
82
|
worker-protocol-version = "1.20"
|
|
83
83
|
control-plane-version = "2"
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Negotiated long-poll responses and early transport failure diagnostics."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import time
|
|
7
|
+
from collections.abc import Mapping
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
import httpx
|
|
11
|
+
|
|
12
|
+
MEDIA_TYPE = "application/vnd.durable-workflow.poll.v1+json"
|
|
13
|
+
ACCEPT = f"{MEDIA_TYPE}, application/json;q=0.9"
|
|
14
|
+
_SCHEMA = "durable-workflow.poll-transport.v1"
|
|
15
|
+
POLL_PATHS = frozenset(
|
|
16
|
+
f"/worker/{kind}-tasks/poll" for kind in ("workflow", "activity", "query", "update-validation")
|
|
17
|
+
)
|
|
18
|
+
_HEADERS = frozenset(("retry-after", "x-durable-workflow-protocol-version"))
|
|
19
|
+
log = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def unwrap_response(response: httpx.Response) -> httpx.Response:
|
|
23
|
+
if response.headers.get("content-type", "").split(";", 1)[0].strip().lower() != MEDIA_TYPE:
|
|
24
|
+
return response
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
envelope = response.json()
|
|
28
|
+
if not isinstance(envelope, dict) or envelope.get("schema") != _SCHEMA:
|
|
29
|
+
raise ValueError("unrecognized poll transport")
|
|
30
|
+
status, headers, body = envelope.get("status"), envelope.get("headers"), envelope.get("body")
|
|
31
|
+
if (type(status) is not int or not 200 <= status < 600
|
|
32
|
+
or response.status_code not in (200, status)
|
|
33
|
+
or not isinstance(body, dict) or not isinstance(headers, dict)
|
|
34
|
+
or any(key not in _HEADERS or not isinstance(value, str)
|
|
35
|
+
or "\r" in value or "\n" in value for key, value in headers.items())):
|
|
36
|
+
raise ValueError("invalid poll transport result")
|
|
37
|
+
# Apply the original status and retry headers before the normal client
|
|
38
|
+
# error handling. Outer HTTP 200 may have been committed by progress.
|
|
39
|
+
return httpx.Response(status, headers=headers, json=body, request=response.request)
|
|
40
|
+
except (ValueError, TypeError) as error:
|
|
41
|
+
raise httpx.RemoteProtocolError("Invalid worker poll transport response.", request=response.request) from error
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def warn_early_cutoff(path: str, body: Any, started: float, error: Exception) -> None:
|
|
45
|
+
wait = body.get("timeout_seconds") if isinstance(body, Mapping) else None
|
|
46
|
+
observed = time.monotonic() - started
|
|
47
|
+
if isinstance(wait, bool) or not isinstance(wait, int | float) or wait <= 0 or observed >= wait:
|
|
48
|
+
return
|
|
49
|
+
if isinstance(error, httpx.HTTPStatusError) and error.response.status_code not in (502, 504):
|
|
50
|
+
return
|
|
51
|
+
log.warning(
|
|
52
|
+
"Worker long poll %s ended after %.3fs before its configured wait of %.3fs (%s). "
|
|
53
|
+
"Check proxy and HTTP timeout budgets.",
|
|
54
|
+
path, observed, wait, type(error).__name__,
|
|
55
|
+
)
|
|
@@ -57,6 +57,10 @@ from ._failure_reporting import (
|
|
|
57
57
|
prepare_failure_request,
|
|
58
58
|
)
|
|
59
59
|
from ._metric_routes import route_for_metrics as _route_for_metrics
|
|
60
|
+
from ._poll_transport import ACCEPT as _POLL_ACCEPT
|
|
61
|
+
from ._poll_transport import POLL_PATHS as _POLL_PATHS
|
|
62
|
+
from ._poll_transport import unwrap_response as _unwrap_poll_response
|
|
63
|
+
from ._poll_transport import warn_early_cutoff as _warn_poll_cutoff
|
|
60
64
|
from ._query_task_authority import QueryTaskAuthority, current_query_authority
|
|
61
65
|
from .errors import (
|
|
62
66
|
REQUEST_ID_CONFLICT,
|
|
@@ -1882,6 +1886,7 @@ class Client:
|
|
|
1882
1886
|
|
|
1883
1887
|
query_poll = worker and path == "/worker/query-tasks/poll"
|
|
1884
1888
|
activity_poll = worker and path == "/worker/activity-tasks/poll"
|
|
1889
|
+
progress_poll = worker and method.upper() == "POST" and path in _POLL_PATHS
|
|
1885
1890
|
if query_poll:
|
|
1886
1891
|
current_query_authority.set(None)
|
|
1887
1892
|
if activity_poll:
|
|
@@ -1931,15 +1936,34 @@ class Client:
|
|
|
1931
1936
|
request_timeout = self.timeout if timeout is None else timeout
|
|
1932
1937
|
if worker and activity_authority is not None and activity_authority.governs(self, self.namespace, path):
|
|
1933
1938
|
request_timeout = min(request_timeout, activity_authority.remaining())
|
|
1934
|
-
|
|
1939
|
+
headers = self._discovery_headers() if discovery else self._headers(worker=worker)
|
|
1940
|
+
if progress_poll:
|
|
1941
|
+
headers["Accept"] = _POLL_ACCEPT
|
|
1942
|
+
pending = self._http.request(
|
|
1935
1943
|
method,
|
|
1936
1944
|
f"/api{path}",
|
|
1937
|
-
headers=
|
|
1945
|
+
headers=headers,
|
|
1938
1946
|
json=request_json,
|
|
1939
1947
|
timeout=request_timeout,
|
|
1940
1948
|
)
|
|
1941
|
-
|
|
1942
|
-
|
|
1949
|
+
try:
|
|
1950
|
+
if progress_poll:
|
|
1951
|
+
try:
|
|
1952
|
+
resp = await asyncio.wait_for(pending, timeout=request_timeout)
|
|
1953
|
+
except asyncio.TimeoutError as error:
|
|
1954
|
+
raise httpx.ReadTimeout(
|
|
1955
|
+
"Worker poll exceeded its absolute HTTP attempt deadline.",
|
|
1956
|
+
request=self._http.build_request(method, f"/api{path}"),
|
|
1957
|
+
) from error
|
|
1958
|
+
resp = _unwrap_poll_response(resp)
|
|
1959
|
+
else:
|
|
1960
|
+
resp = await pending
|
|
1961
|
+
# Interpret negotiated late errors before the usual retry policy.
|
|
1962
|
+
resp.raise_for_status()
|
|
1963
|
+
except (httpx.TransportError, httpx.HTTPStatusError) as error:
|
|
1964
|
+
if progress_poll:
|
|
1965
|
+
_warn_poll_cutoff(path, request_json, request_started, error)
|
|
1966
|
+
raise
|
|
1943
1967
|
return resp
|
|
1944
1968
|
|
|
1945
1969
|
try:
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow.egg-info/SOURCES.txt
RENAMED
|
@@ -10,6 +10,7 @@ src/durable_workflow/_cancellation_scope_history.py
|
|
|
10
10
|
src/durable_workflow/_cooperative_cancellation.py
|
|
11
11
|
src/durable_workflow/_failure_reporting.py
|
|
12
12
|
src/durable_workflow/_metric_routes.py
|
|
13
|
+
src/durable_workflow/_poll_transport.py
|
|
13
14
|
src/durable_workflow/_prepared_local_activity.py
|
|
14
15
|
src/durable_workflow/_query_task_authority.py
|
|
15
16
|
src/durable_workflow/_sticky_workflow_cache.py
|
|
@@ -104,6 +105,7 @@ tests/test_parallel_saga_authoring.py
|
|
|
104
105
|
tests/test_patch_history.py
|
|
105
106
|
tests/test_pending_operation_replay.py
|
|
106
107
|
tests/test_poll_failure_backoff.py
|
|
108
|
+
tests/test_poll_transport.py
|
|
107
109
|
tests/test_prepared_local_activity_client.py
|
|
108
110
|
tests/test_prepared_local_activity_groups.py
|
|
109
111
|
tests/test_prepared_local_activity_policies.py
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
import logging
|
|
6
|
+
import time
|
|
7
|
+
from collections.abc import AsyncIterator
|
|
8
|
+
from contextlib import asynccontextmanager, suppress
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
import httpx
|
|
12
|
+
import pytest
|
|
13
|
+
|
|
14
|
+
from durable_workflow._poll_transport import MEDIA_TYPE, unwrap_response
|
|
15
|
+
from durable_workflow.client import Client
|
|
16
|
+
from durable_workflow.errors import InvalidArgument, ServerError
|
|
17
|
+
from durable_workflow.retry_policy import TransportRetryPolicy
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def envelope(status: int, body: Any, **changes: Any) -> dict[str, Any]:
|
|
21
|
+
return {"schema": "durable-workflow.poll-transport.v1", "status": status, "headers": {},
|
|
22
|
+
"body": body, **changes}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def wire_response(body: Any, *, status: int = 200) -> httpx.Response:
|
|
26
|
+
return httpx.Response(status, headers={"content-type": MEDIA_TYPE.upper() + "; charset=utf-8"},
|
|
27
|
+
json=body, request=httpx.Request("POST", "http://fixture/api/worker/workflow-tasks/poll"))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@pytest.mark.asyncio
|
|
31
|
+
@pytest.mark.parametrize("kind", ["workflow", "activity", "query", "update-validation"])
|
|
32
|
+
@pytest.mark.parametrize("negotiated", [False, True])
|
|
33
|
+
async def test_poll_negotiation_accepts_legacy_and_progress(kind: str, negotiated: bool) -> None:
|
|
34
|
+
async def serve(request: httpx.Request) -> httpx.Response:
|
|
35
|
+
assert request.headers["accept"] == MEDIA_TYPE + ", application/json;q=0.9"
|
|
36
|
+
data = {"task": None, "poll_status": "empty"}
|
|
37
|
+
if negotiated:
|
|
38
|
+
return httpx.Response(200, headers={"content-type": MEDIA_TYPE},
|
|
39
|
+
content=b"\n\n" + json.dumps(envelope(200, data)).encode())
|
|
40
|
+
return httpx.Response(200, json=data)
|
|
41
|
+
|
|
42
|
+
async with Client("http://fixture") as client:
|
|
43
|
+
await client._http.aclose()
|
|
44
|
+
client._http = httpx.AsyncClient(base_url="http://fixture", transport=httpx.MockTransport(serve))
|
|
45
|
+
poll = getattr(client, f"poll_{kind.replace('-', '_')}_task")
|
|
46
|
+
assert await poll(worker_id="fixture-worker", task_queue="fixture-queue", timeout=0) is None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@pytest.mark.asyncio
|
|
50
|
+
async def test_non_poll_request_keeps_ordinary_response() -> None:
|
|
51
|
+
async def serve(request: httpx.Request) -> httpx.Response:
|
|
52
|
+
assert MEDIA_TYPE not in request.headers.get("accept", "")
|
|
53
|
+
return httpx.Response(200, json={"healthy": True})
|
|
54
|
+
|
|
55
|
+
async with Client("http://fixture") as client:
|
|
56
|
+
await client._http.aclose()
|
|
57
|
+
client._http = httpx.AsyncClient(base_url="http://fixture", transport=httpx.MockTransport(serve))
|
|
58
|
+
assert await client.health() == {"healthy": True}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_unwrap_preserves_task_values_and_original_headers() -> None:
|
|
62
|
+
body = {"task": {"counter": 9007199254740993, "value": 1.0, "payload": {"codec": "avro", "data": "AA=="}}}
|
|
63
|
+
response = unwrap_response(wire_response(envelope(200, body, headers={
|
|
64
|
+
"x-durable-workflow-protocol-version": "1.20", "retry-after": "2",
|
|
65
|
+
})))
|
|
66
|
+
assert response.json() == body
|
|
67
|
+
assert response.headers["x-durable-workflow-protocol-version"] == "1.20"
|
|
68
|
+
assert response.headers["retry-after"] == "2"
|
|
69
|
+
assert len(response.content) == int(response.headers["content-length"])
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@pytest.mark.parametrize("change", [
|
|
73
|
+
{"schema": "unknown"}, {"status": True}, {"status": 199}, {"status": 600},
|
|
74
|
+
{"body": []}, {"headers": []}, {"headers": {"retry-after": 2}},
|
|
75
|
+
{"headers": {"authorization": "not-allowed"}}, {"headers": {"retry-after": "2\r\nX-Test: bad"}},
|
|
76
|
+
])
|
|
77
|
+
def test_invalid_transport_is_not_silently_treated_as_an_empty_poll(change: dict[str, Any]) -> None:
|
|
78
|
+
response = wire_response(envelope(200, {"task": None}) | change)
|
|
79
|
+
with pytest.raises(httpx.RemoteProtocolError, match="Invalid worker poll transport"):
|
|
80
|
+
unwrap_response(response)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_outer_http_error_cannot_be_rewritten_as_success() -> None:
|
|
84
|
+
response = wire_response(envelope(200, {"task": None}), status=503)
|
|
85
|
+
with pytest.raises(httpx.RemoteProtocolError):
|
|
86
|
+
unwrap_response(response)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@pytest.mark.asyncio
|
|
90
|
+
@pytest.mark.parametrize("status", [409, 422, 429, 503])
|
|
91
|
+
async def test_late_errors_retain_typed_error_and_retry_semantics(status: int) -> None:
|
|
92
|
+
attempts = []
|
|
93
|
+
retry_policy = TransportRetryPolicy(max_attempts=2, initial_backoff_seconds=0, jitter=False)
|
|
94
|
+
|
|
95
|
+
async def serve(request: httpx.Request) -> httpx.Response:
|
|
96
|
+
attempts.append(json.loads(request.content)["poll_request_id"])
|
|
97
|
+
reason = "long_poll_capacity_exhausted" if status == 429 else "fixture_late_failure"
|
|
98
|
+
body = {"error": "fixture failure", "reason": reason, "retryable": True}
|
|
99
|
+
if status == 429:
|
|
100
|
+
body.update({"task": None, "poll_status": reason, "task_kind": "workflow_task",
|
|
101
|
+
"task_queue": "fixture-queue", "retry_after_seconds": 2})
|
|
102
|
+
return httpx.Response(200, headers={"content-type": MEDIA_TYPE}, json=envelope(
|
|
103
|
+
status, body,
|
|
104
|
+
headers={"retry-after": "2"},
|
|
105
|
+
))
|
|
106
|
+
|
|
107
|
+
async with Client("http://fixture", retry_policy=retry_policy) as client:
|
|
108
|
+
await client._http.aclose()
|
|
109
|
+
client._http = httpx.AsyncClient(base_url="http://fixture", transport=httpx.MockTransport(serve))
|
|
110
|
+
with pytest.raises(InvalidArgument if status == 422 else ServerError) as raised:
|
|
111
|
+
await client.poll_workflow_task(worker_id="fixture-worker", task_queue="fixture-queue", timeout=0)
|
|
112
|
+
if isinstance(raised.value, ServerError):
|
|
113
|
+
assert raised.value.status == status
|
|
114
|
+
assert raised.value.reason() == ("long_poll_capacity_exhausted" if status == 429 else "fixture_late_failure")
|
|
115
|
+
assert len(attempts) == (2 if status == 503 else 1)
|
|
116
|
+
assert len(set(attempts)) == 1
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@asynccontextmanager
|
|
120
|
+
async def progressing_peer(
|
|
121
|
+
*, complete_after: float, requests: list[dict[str, Any]] | None = None,
|
|
122
|
+
) -> AsyncIterator[tuple[str, list[asyncio.Event]]]:
|
|
123
|
+
closed = asyncio.Event()
|
|
124
|
+
started = asyncio.Event()
|
|
125
|
+
handlers: set[asyncio.Task[None]] = set()
|
|
126
|
+
|
|
127
|
+
async def serve(reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
|
|
128
|
+
task = asyncio.current_task()
|
|
129
|
+
assert task is not None
|
|
130
|
+
handlers.add(task)
|
|
131
|
+
try:
|
|
132
|
+
headers = await reader.readuntil(b"\r\n\r\n")
|
|
133
|
+
for header in headers.split(b"\r\n"):
|
|
134
|
+
if header.lower().startswith(b"content-length:"):
|
|
135
|
+
body = await reader.readexactly(int(header.split(b":", 1)[1]))
|
|
136
|
+
if requests is not None:
|
|
137
|
+
requests.append(json.loads(body))
|
|
138
|
+
writer.write(b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nConnection: close\r\n"
|
|
139
|
+
+ f"Content-Type: {MEDIA_TYPE}\r\n\r\n".encode())
|
|
140
|
+
started.set()
|
|
141
|
+
finish = time.monotonic() + complete_after
|
|
142
|
+
while time.monotonic() < finish:
|
|
143
|
+
writer.write(b"1\r\n\n\r\n")
|
|
144
|
+
await writer.drain()
|
|
145
|
+
if reader.at_eof():
|
|
146
|
+
return
|
|
147
|
+
await asyncio.sleep(0.01)
|
|
148
|
+
if not reader.at_eof():
|
|
149
|
+
data = json.dumps(envelope(200, {"task": None, "poll_status": "empty"})).encode()
|
|
150
|
+
writer.write(f"{len(data):x}\r\n".encode() + data + b"\r\n0\r\n\r\n")
|
|
151
|
+
await writer.drain()
|
|
152
|
+
except (ConnectionError, asyncio.IncompleteReadError):
|
|
153
|
+
pass
|
|
154
|
+
finally:
|
|
155
|
+
writer.close()
|
|
156
|
+
with suppress(ConnectionError):
|
|
157
|
+
await writer.wait_closed()
|
|
158
|
+
handlers.discard(task)
|
|
159
|
+
closed.set()
|
|
160
|
+
|
|
161
|
+
server = await asyncio.start_server(serve, "127.0.0.1", 0)
|
|
162
|
+
try:
|
|
163
|
+
assert server.sockets
|
|
164
|
+
yield f"http://127.0.0.1:{server.sockets[0].getsockname()[1]}", [started, closed]
|
|
165
|
+
finally:
|
|
166
|
+
server.close()
|
|
167
|
+
await server.wait_closed()
|
|
168
|
+
for task in list(handlers):
|
|
169
|
+
task.cancel()
|
|
170
|
+
if handlers:
|
|
171
|
+
await asyncio.gather(*handlers, return_exceptions=True)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
@pytest.mark.asyncio
|
|
175
|
+
@pytest.mark.parametrize("kind", ["workflow", "activity", "query", "update-validation"])
|
|
176
|
+
async def test_progress_cannot_renew_the_absolute_attempt_deadline(
|
|
177
|
+
kind: str, caplog: pytest.LogCaptureFixture,
|
|
178
|
+
) -> None:
|
|
179
|
+
async with (
|
|
180
|
+
progressing_peer(complete_after=0.6) as (endpoint, events),
|
|
181
|
+
Client(endpoint, retry_policy=TransportRetryPolicy(max_attempts=1)) as client,
|
|
182
|
+
):
|
|
183
|
+
started = time.monotonic()
|
|
184
|
+
request = client._request("POST", f"/worker/{kind}-tasks/poll", worker=True,
|
|
185
|
+
json={"timeout_seconds": 30}, timeout=0.12)
|
|
186
|
+
with caplog.at_level(logging.WARNING), pytest.raises(httpx.ReadTimeout, match="absolute HTTP attempt"):
|
|
187
|
+
await asyncio.wait_for(request, timeout=2)
|
|
188
|
+
assert time.monotonic() - started < 0.5
|
|
189
|
+
await asyncio.wait_for(events[1].wait(), timeout=1)
|
|
190
|
+
assert "configured wait of 30.000s" in caplog.text
|
|
191
|
+
assert "ended after" in caplog.text
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@pytest.mark.asyncio
|
|
195
|
+
async def test_public_poll_keeps_its_retry_budget_and_logical_identity() -> None:
|
|
196
|
+
requests: list[dict[str, Any]] = []
|
|
197
|
+
async with (
|
|
198
|
+
progressing_peer(complete_after=1.2, requests=requests) as (endpoint, _events),
|
|
199
|
+
Client(endpoint, retry_policy=TransportRetryPolicy(max_attempts=1)) as client,
|
|
200
|
+
):
|
|
201
|
+
started = time.monotonic()
|
|
202
|
+
result = await client.poll_workflow_task_response(
|
|
203
|
+
worker_id="fixture-worker", task_queue="fixture-queue", timeout=0,
|
|
204
|
+
)
|
|
205
|
+
assert result == {"task": None, "poll_status": "timeout"}
|
|
206
|
+
assert 1.8 < time.monotonic() - started < 3
|
|
207
|
+
assert len(requests) == 2
|
|
208
|
+
assert len({request["poll_request_id"] for request in requests}) == 1
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@pytest.mark.asyncio
|
|
212
|
+
@pytest.mark.parametrize("status", [502, 504])
|
|
213
|
+
async def test_gateway_cutoff_warns_with_observed_and_configured_wait(
|
|
214
|
+
status: int, caplog: pytest.LogCaptureFixture,
|
|
215
|
+
) -> None:
|
|
216
|
+
async def serve(_request: httpx.Request) -> httpx.Response:
|
|
217
|
+
return httpx.Response(status, text="fixture gateway error")
|
|
218
|
+
|
|
219
|
+
async with Client("http://fixture", retry_policy=TransportRetryPolicy(max_attempts=1)) as client:
|
|
220
|
+
await client._http.aclose()
|
|
221
|
+
client._http = httpx.AsyncClient(base_url="http://fixture", transport=httpx.MockTransport(serve))
|
|
222
|
+
with caplog.at_level(logging.WARNING), pytest.raises(ServerError) as raised:
|
|
223
|
+
await client.poll_workflow_task(worker_id="fixture-worker", task_queue="fixture-queue", timeout=30)
|
|
224
|
+
assert raised.value.status == status
|
|
225
|
+
assert "configured wait of 30.000s" in caplog.text
|
|
226
|
+
assert "ended after" in caplog.text
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
@pytest.mark.asyncio
|
|
230
|
+
async def test_progress_completes_inside_the_attempt_deadline() -> None:
|
|
231
|
+
async with (
|
|
232
|
+
progressing_peer(complete_after=0.12) as (endpoint, _events),
|
|
233
|
+
Client(endpoint, retry_policy=TransportRetryPolicy(max_attempts=1)) as client,
|
|
234
|
+
):
|
|
235
|
+
result = await client._request("POST", "/worker/workflow-tasks/poll", worker=True,
|
|
236
|
+
json={"timeout_seconds": 1}, timeout=0.5)
|
|
237
|
+
assert result == {"task": None, "poll_status": "empty"}
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
@pytest.mark.asyncio
|
|
241
|
+
async def test_cancellation_closes_a_progressing_request_without_a_cutoff_warning(
|
|
242
|
+
caplog: pytest.LogCaptureFixture,
|
|
243
|
+
) -> None:
|
|
244
|
+
async with (
|
|
245
|
+
progressing_peer(complete_after=1) as (endpoint, events),
|
|
246
|
+
Client(endpoint, retry_policy=TransportRetryPolicy(max_attempts=1)) as client,
|
|
247
|
+
):
|
|
248
|
+
request = asyncio.create_task(client._request("POST", "/worker/workflow-tasks/poll", worker=True,
|
|
249
|
+
json={"timeout_seconds": 30}, timeout=5))
|
|
250
|
+
await asyncio.wait_for(events[0].wait(), timeout=1)
|
|
251
|
+
request.cancel()
|
|
252
|
+
with pytest.raises(asyncio.CancelledError):
|
|
253
|
+
await request
|
|
254
|
+
await asyncio.wait_for(events[1].wait(), timeout=1)
|
|
255
|
+
assert "ended after" not in caplog.text
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/_activity_process.py
RENAMED
|
File without changes
|
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/_cancellation_scope.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/_failure_reporting.py
RENAMED
|
File without changes
|
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/_prepared_local_activity.py
RENAMED
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/_query_task_authority.py
RENAMED
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/_sticky_workflow_cache.py
RENAMED
|
File without changes
|
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/auth_composition.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/external_storage.py
RENAMED
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/external_task_input.py
RENAMED
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/external_task_result.py
RENAMED
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/history_bundle_verify.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/python_conformance.py
RENAMED
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/replay_conformance.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow/telemetry_carrier.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow.egg-info/requires.txt
RENAMED
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/src/durable_workflow.egg-info/top_level.txt
RENAMED
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_activity_cancellation_policies.py
RENAMED
|
File without changes
|
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_activity_heartbeat_authority.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_cancellation_remaining_time.py
RENAMED
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_cancellation_scope_admission.py
RENAMED
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_cancellation_scope_authoring.py
RENAMED
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_cancellation_scope_boundary.py
RENAMED
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_cancellation_scope_opening.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_control_plane_parity_fixtures.py
RENAMED
|
File without changes
|
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_cooperative_cancellation_client.py
RENAMED
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_cooperative_cancellation_worker.py
RENAMED
|
File without changes
|
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_docstring_cross_references.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_parallel_completion_order_replay.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_prepared_local_activity_client.py
RENAMED
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_prepared_local_activity_groups.py
RENAMED
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_prepared_local_activity_policies.py
RENAMED
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_prepared_local_activity_worker.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_run_cancellation_scope_replay.py
RENAMED
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_runtime_external_payload_transport.py
RENAMED
|
File without changes
|
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_scoped_run_cancellation_context.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_standalone_activity_client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_threaded_heartbeat_example.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_update_signal_condition_replay.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_workflow_entrypoint_validation.py
RENAMED
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_workflow_handler_validation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{durable_workflow-2.11.1 → durable_workflow-2.11.2}/tests/test_workflow_updates_conformance.py
RENAMED
|
File without changes
|