Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ lint-docs = "uv run pydocstyle --ignore-decorators=overload"
lint-types = [
{ cmd = "uv run pyright" },
{ cmd = "uv run mypy --namespace-packages --check-untyped-defs ." },
{ cmd = "uv run basedpyright" }
{ cmd = "uv run basedpyright" },
]
run-bench = "uv run python scripts/run_bench.py"
test = "uv run pytest"
Expand Down Expand Up @@ -229,3 +229,6 @@ exclude = ["temporalio/bridge/target/**/*"]
[tool.uv]
# Prevent uv commands from building the package by default
package = false

[tool.uv.sources]
nexus-rpc = { git = "https://github.com/nexus-rpc/sdk-python.git", branch = "preserve-raw-handler-error-type" }
11 changes: 2 additions & 9 deletions temporalio/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -1079,7 +1079,7 @@ def _nexus_handler_error_to_failure(
if error.__cause__:
self.to_failure(error.__cause__, payload_converter, failure.cause)
failure.nexus_handler_failure_info.SetInParent()
failure.nexus_handler_failure_info.type = error.type.name
failure.nexus_handler_failure_info.type = error.error_type.name
failure.nexus_handler_failure_info.retry_behavior = temporalio.api.enums.v1.NexusHandlerErrorRetryBehavior.ValueType(
temporalio.api.enums.v1.NexusHandlerErrorRetryBehavior.NEXUS_HANDLER_ERROR_RETRY_BEHAVIOR_RETRYABLE
if error.retryable_override is True
Expand Down Expand Up @@ -1184,13 +1184,6 @@ def from_failure(
)
elif failure.HasField("nexus_handler_failure_info"):
nexus_handler_failure_info = failure.nexus_handler_failure_info
try:
_type = nexusrpc.HandlerErrorType[nexus_handler_failure_info.type]
except KeyError:
logger.warning(
f"Unknown Nexus HandlerErrorType: {nexus_handler_failure_info.type}"
)
_type = nexusrpc.HandlerErrorType.INTERNAL
retryable_override = (
True
if (
Expand All @@ -1206,7 +1199,7 @@ def from_failure(
)
err = nexusrpc.HandlerError(
failure.message or "Nexus handler error",
type=_type,
error_type=nexus_handler_failure_info.type,
retryable_override=retryable_override,
)
elif failure.HasField("nexus_operation_execution_failure_info"):
Expand Down
4 changes: 2 additions & 2 deletions temporalio/nexus/_operation_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ async def _cancel_workflow(
raise HandlerError(
"Failed to decode operation token as a workflow operation token. "
"Canceling non-workflow operations is not supported.",
type=HandlerErrorType.NOT_FOUND,
error_type=HandlerErrorType.NOT_FOUND,
) from err

ctx = _temporal_cancel_operation_context.get()
Expand All @@ -109,6 +109,6 @@ async def _cancel_workflow(
except Exception as err:
raise HandlerError(
"Failed to construct workflow handle from workflow operation token",
type=HandlerErrorType.NOT_FOUND,
error_type=HandlerErrorType.NOT_FOUND,
) from err
await client_workflow_handle.cancel(**kwargs)
28 changes: 14 additions & 14 deletions temporalio/worker/_nexus.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ async def _handler_error_to_proto(
else temporalio.api.enums.v1.NexusHandlerErrorRetryBehavior.NEXUS_HANDLER_ERROR_RETRY_BEHAVIOR_UNSPECIFIED
)
return temporalio.api.nexus.v1.HandlerError(
error_type=handler_error.type.value,
error_type=handler_error.error_type.value,
failure=await self._nexus_error_to_nexus_failure_proto(handler_error),
retry_behavior=retry_behavior,
)
Expand Down Expand Up @@ -480,7 +480,7 @@ async def deserialize(
except Exception as err:
raise nexusrpc.HandlerError(
"Data converter failed to decode Nexus operation input",
type=nexusrpc.HandlerErrorType.BAD_REQUEST,
error_type=nexusrpc.HandlerErrorType.BAD_REQUEST,
retryable_override=False,
) from err

Expand Down Expand Up @@ -509,20 +509,20 @@ def _exception_to_handler_error(err: BaseException) -> nexusrpc.HandlerError:
handler_err = nexusrpc.HandlerError(
# TODO(nexus-preview): confirm what we want as message here
err.message,
type=nexusrpc.HandlerErrorType.INTERNAL,
error_type=nexusrpc.HandlerErrorType.INTERNAL,
retryable_override=not err.non_retryable,
)
elif isinstance(err, WorkflowAlreadyStartedError):
handler_err = nexusrpc.HandlerError(
err.message,
type=nexusrpc.HandlerErrorType.INTERNAL,
error_type=nexusrpc.HandlerErrorType.INTERNAL,
retryable_override=False,
)
elif isinstance(err, RPCError):
if err.status == RPCStatusCode.INVALID_ARGUMENT:
handler_err = nexusrpc.HandlerError(
err.message,
type=nexusrpc.HandlerErrorType.BAD_REQUEST,
error_type=nexusrpc.HandlerErrorType.BAD_REQUEST,
)
elif err.status in [
RPCStatusCode.ALREADY_EXISTS,
Expand All @@ -531,13 +531,13 @@ def _exception_to_handler_error(err: BaseException) -> nexusrpc.HandlerError:
]:
handler_err = nexusrpc.HandlerError(
err.message,
type=nexusrpc.HandlerErrorType.INTERNAL,
error_type=nexusrpc.HandlerErrorType.INTERNAL,
retryable_override=False,
)
elif err.status in [RPCStatusCode.ABORTED, RPCStatusCode.UNAVAILABLE]:
handler_err = nexusrpc.HandlerError(
err.message,
type=nexusrpc.HandlerErrorType.UNAVAILABLE,
error_type=nexusrpc.HandlerErrorType.UNAVAILABLE,
)
elif err.status in [
RPCStatusCode.CANCELLED,
Expand All @@ -552,35 +552,35 @@ def _exception_to_handler_error(err: BaseException) -> nexusrpc.HandlerError:
# when the handler fails to auth with Temporal and should be considered
# retryable.
handler_err = nexusrpc.HandlerError(
err.message, type=nexusrpc.HandlerErrorType.INTERNAL
err.message, error_type=nexusrpc.HandlerErrorType.INTERNAL
)
elif err.status == RPCStatusCode.NOT_FOUND:
handler_err = nexusrpc.HandlerError(
err.message, type=nexusrpc.HandlerErrorType.NOT_FOUND
err.message, error_type=nexusrpc.HandlerErrorType.NOT_FOUND
)
elif err.status == RPCStatusCode.RESOURCE_EXHAUSTED:
handler_err = nexusrpc.HandlerError(
err.message,
type=nexusrpc.HandlerErrorType.RESOURCE_EXHAUSTED,
error_type=nexusrpc.HandlerErrorType.RESOURCE_EXHAUSTED,
)
elif err.status == RPCStatusCode.UNIMPLEMENTED:
handler_err = nexusrpc.HandlerError(
err.message,
type=nexusrpc.HandlerErrorType.NOT_IMPLEMENTED,
error_type=nexusrpc.HandlerErrorType.NOT_IMPLEMENTED,
)
elif err.status == RPCStatusCode.DEADLINE_EXCEEDED:
handler_err = nexusrpc.HandlerError(
err.message,
type=nexusrpc.HandlerErrorType.UPSTREAM_TIMEOUT,
error_type=nexusrpc.HandlerErrorType.UPSTREAM_TIMEOUT,
)
else:
handler_err = nexusrpc.HandlerError(
f"Unhandled RPC error status: {err.status}",
type=nexusrpc.HandlerErrorType.INTERNAL,
error_type=nexusrpc.HandlerErrorType.INTERNAL,
)
else:
handler_err = nexusrpc.HandlerError(
str(err), type=nexusrpc.HandlerErrorType.INTERNAL
str(err), error_type=nexusrpc.HandlerErrorType.INTERNAL
)
handler_err.__cause__ = err
return handler_err
Expand Down
2 changes: 1 addition & 1 deletion tests/nexus/test_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ async def handler_error_internal(
) -> Output:
raise HandlerError(
message="deliberate internal handler error",
type=HandlerErrorType.INTERNAL,
error_type=HandlerErrorType.INTERNAL,
retryable_override=False,
) from RuntimeError("cause message")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ async def cancel(
test_context.cancel_handler_released.set_result(datetime.now(timezone.utc))
raise nexusrpc.HandlerError(
"Deliberate non-retryable error in cancel handler",
type=nexusrpc.HandlerErrorType.BAD_REQUEST,
error_type=nexusrpc.HandlerErrorType.BAD_REQUEST,
)


Expand Down
14 changes: 7 additions & 7 deletions tests/nexus/test_workflow_caller_error_chains.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def action_in_nexus_operation():
# that of the ApplicationError. The server prepends 'handler error
# (INTERNAL):'
"message": "handler error (INTERNAL): application-error-message",
"type": nexusrpc.HandlerErrorType.INTERNAL,
"error_type": nexusrpc.HandlerErrorType.INTERNAL,
"retryable": False,
},
),
Expand Down Expand Up @@ -147,7 +147,7 @@ def action_in_nexus_operation():
except RuntimeError as err:
raise nexusrpc.HandlerError(
"handler-error-message",
type=nexusrpc.HandlerErrorType.NOT_FOUND,
error_type=nexusrpc.HandlerErrorType.NOT_FOUND,
) from err

expected_exception_chain_in_workflow = [
Expand All @@ -165,7 +165,7 @@ def action_in_nexus_operation():
# was no need to synthesize a wrapping HandlerError The server prepends
# 'handler error (INTERNAL):'
"message": "handler error (NOT_FOUND): handler-error-message",
"type": nexusrpc.HandlerErrorType.NOT_FOUND,
"error_type": nexusrpc.HandlerErrorType.NOT_FOUND,
# The following HandlerError types should be considered non-retryable:
# BAD_REQUEST, UNAUTHENTICATED, UNAUTHORIZED, NOT_FOUND, and
# RESOURCE_EXHAUSTED. In this test case, the handler does not set the
Expand Down Expand Up @@ -200,7 +200,7 @@ def action_in_nexus_operation():
except CustomError as err:
raise nexusrpc.HandlerError(
"handler-error-message",
type=nexusrpc.HandlerErrorType.NOT_FOUND,
error_type=nexusrpc.HandlerErrorType.NOT_FOUND,
) from err

expected_exception_chain_in_workflow = (
Expand Down Expand Up @@ -228,12 +228,12 @@ def action_in_nexus_operation():
try:
raise nexusrpc.HandlerError(
"handler-error-message-2",
type=nexusrpc.HandlerErrorType.UNAVAILABLE,
error_type=nexusrpc.HandlerErrorType.UNAVAILABLE,
)
except nexusrpc.HandlerError as err:
raise nexusrpc.HandlerError(
"handler-error-message",
type=nexusrpc.HandlerErrorType.NOT_FOUND,
error_type=nexusrpc.HandlerErrorType.NOT_FOUND,
) from err

expected_exception_chain_in_workflow = (
Expand All @@ -243,7 +243,7 @@ def action_in_nexus_operation():
nexusrpc.HandlerError,
{
"message": "handler-error-message-2",
"type": nexusrpc.HandlerErrorType.UNAVAILABLE,
"error_type": nexusrpc.HandlerErrorType.UNAVAILABLE,
"retryable": True,
},
)
Expand Down
6 changes: 3 additions & 3 deletions tests/nexus/test_workflow_caller_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def retried_due_to_resource_exhausted_handler_error(
operation_invocation_counts[input.id] += 1
raise nexusrpc.HandlerError(
"handler-error-message",
type=nexusrpc.HandlerErrorType.RESOURCE_EXHAUSTED,
error_type=nexusrpc.HandlerErrorType.RESOURCE_EXHAUSTED,
)

@nexusrpc.handler.sync_operation
Expand All @@ -94,7 +94,7 @@ def retried_due_to_internal_handler_error(
operation_invocation_counts[input.id] += 1
raise nexusrpc.HandlerError(
"handler-error-message",
type=nexusrpc.HandlerErrorType.INTERNAL,
error_type=nexusrpc.HandlerErrorType.INTERNAL,
)

@nexusrpc.handler.sync_operation
Expand Down Expand Up @@ -226,7 +226,7 @@ async def test_nexus_operation_fails_without_retry_as_handler_error(
handler_error = err.__cause__.__cause__
assert isinstance(handler_error, nexusrpc.HandlerError)
assert not handler_error.retryable
assert handler_error.type == handler_error_type
assert handler_error.error_type == handler_error_type
assert handler_error_message in str(handler_error)
else:
pytest.fail("Unreachable")
Expand Down
8 changes: 2 additions & 6 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.