From 1bb70650a0861155380cb62fc106eab2e3288893 Mon Sep 17 00:00:00 2001 From: James Watkins-Harvey Date: Mon, 27 Oct 2025 16:44:04 -0400 Subject: [PATCH 1/6] Add request deadline to OperationContext subclasses --- src/nexusrpc/handler/_common.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/nexusrpc/handler/_common.py b/src/nexusrpc/handler/_common.py index 8fcbc59..3b889d8 100644 --- a/src/nexusrpc/handler/_common.py +++ b/src/nexusrpc/handler/_common.py @@ -3,6 +3,7 @@ from abc import ABC from collections.abc import Mapping, Sequence from dataclasses import dataclass, field +from datetime import datetime from typing import Any, Generic, Optional from nexusrpc._common import Link, OutputT @@ -49,6 +50,12 @@ class StartOperationContext(OperationContext): By default a v4 UUID should be generated by the client. """ + request_deadline: Optional[datetime] = None + """ + Get the deadline for the operation handler method. Note that this is the time by which the + current _request_ should complete, not the _operation_'s deadline. + """ + callback_url: Optional[str] = None """ A callback URL is required to deliver the completion of an async operation. This URL should be @@ -82,6 +89,12 @@ class CancelOperationContext(OperationContext): Includes information from the request.""" + request_deadline: Optional[datetime] = None + """ + Get the deadline for the operation handler method. Note that this is the time by which the + current _request_ should complete, not the _operation_'s deadline. + """ + @dataclass(frozen=True) class StartOperationResultSync(Generic[OutputT]): From 0e13db6328f46a998e4040571b9154c55bc9d1d8 Mon Sep 17 00:00:00 2001 From: James Watkins-Harvey Date: Fri, 21 Nov 2025 11:32:39 -0500 Subject: [PATCH 2/6] Move request_deadline to OperationContext --- src/nexusrpc/handler/_common.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/nexusrpc/handler/_common.py b/src/nexusrpc/handler/_common.py index 699a951..1c4eb21 100644 --- a/src/nexusrpc/handler/_common.py +++ b/src/nexusrpc/handler/_common.py @@ -68,11 +68,18 @@ def __new__(cls, *args: Any, **kwargs: Any): """ Optional header fields sent by the caller. """ + task_cancellation: OperationTaskCancellation """ Task cancellation information indicating that a running task should be interrupted. This is distinct from operation cancellation. """ + request_deadline: Optional[datetime] = field(default=None, kw_only=True) + """ + The deadline for the operation handler method. Note that this is the time by which the + current _request_ should complete, not the _operation_'s deadline. + """ + @dataclass(frozen=True) class StartOperationContext(OperationContext): @@ -86,12 +93,6 @@ class StartOperationContext(OperationContext): By default a v4 UUID should be generated by the client. """ - request_deadline: Optional[datetime] = None - """ - Get the deadline for the operation handler method. Note that this is the time by which the - current _request_ should complete, not the _operation_'s deadline. - """ - callback_url: Optional[str] = None """ A callback URL is required to deliver the completion of an async operation. This URL should be @@ -125,12 +126,6 @@ class CancelOperationContext(OperationContext): Includes information from the request.""" - request_deadline: Optional[datetime] = None - """ - Get the deadline for the operation handler method. Note that this is the time by which the - current _request_ should complete, not the _operation_'s deadline. - """ - @dataclass(frozen=True) class StartOperationResultSync(Generic[OutputT]): From f3c006de017ae82a8a17cbd3692dbddaa9406f92 Mon Sep 17 00:00:00 2001 From: James Watkins-Harvey Date: Tue, 25 Nov 2025 15:33:16 -0500 Subject: [PATCH 3/6] Make all props on OperationContext kwonly --- src/nexusrpc/handler/_common.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/nexusrpc/handler/_common.py b/src/nexusrpc/handler/_common.py index 1c4eb21..2324be6 100644 --- a/src/nexusrpc/handler/_common.py +++ b/src/nexusrpc/handler/_common.py @@ -54,22 +54,22 @@ def __new__(cls, *args: Any, **kwargs: Any): ) return super().__new__(cls) - service: str + service: str = field(kw_only=True) """ The name of the service that the operation belongs to. """ - operation: str + operation: str = field(kw_only=True) """ The name of the operation. """ - headers: Mapping[str, str] + headers: Mapping[str, str] = field(default_factory=dict, kw_only=True) """ Optional header fields sent by the caller. """ - task_cancellation: OperationTaskCancellation + task_cancellation: OperationTaskCancellation = field(kw_only=True) """ Task cancellation information indicating that a running task should be interrupted. This is distinct from operation cancellation. """ @@ -87,32 +87,32 @@ class StartOperationContext(OperationContext): Includes information from the request.""" - request_id: str + request_id: str = field(kw_only=True) """ Request ID that may be used by the handler to dedupe a start request. By default a v4 UUID should be generated by the client. """ - callback_url: Optional[str] = None + callback_url: Optional[str] = field(default=None, kw_only=True) """ A callback URL is required to deliver the completion of an async operation. This URL should be called by a handler upon completion if the started operation is async. """ - callback_headers: Mapping[str, str] = field(default_factory=dict) + callback_headers: Mapping[str, str] = field(default_factory=dict, kw_only=True) """ Optional header fields set by the caller to be attached to the callback request when an asynchronous operation completes. """ - inbound_links: Sequence[Link] = field(default_factory=list) + inbound_links: Sequence[Link] = field(default_factory=list, kw_only=True) """ Links received in the request. This list is automatically populated when handling a start request. Handlers may use these links, for example to add information about the caller to a resource associated with the operation execution. """ - outbound_links: list[Link] = field(default_factory=list) + outbound_links: list[Link] = field(default_factory=list, kw_only=True) """ Links to be returned by the handler. This list is initially empty. Handlers may populate this list, for example with links describing resources associated with the From 56da1b87401453760ffadd6b75b95bd5cdd5f5a3 Mon Sep 17 00:00:00 2001 From: James Watkins-Harvey Date: Tue, 25 Nov 2025 16:27:55 -0500 Subject: [PATCH 4/6] Only kw_args on the class, not on individual fields --- src/nexusrpc/handler/_common.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/nexusrpc/handler/_common.py b/src/nexusrpc/handler/_common.py index 2324be6..48a88ca 100644 --- a/src/nexusrpc/handler/_common.py +++ b/src/nexusrpc/handler/_common.py @@ -41,7 +41,7 @@ async def wait_until_cancelled(self) -> None: raise NotImplementedError -@dataclass(frozen=True) +@dataclass(frozen=True, kw_only=True) class OperationContext(ABC): """Context for the execution of the requested operation method. @@ -54,65 +54,65 @@ def __new__(cls, *args: Any, **kwargs: Any): ) return super().__new__(cls) - service: str = field(kw_only=True) + service: str """ The name of the service that the operation belongs to. """ - operation: str = field(kw_only=True) + operation: str """ The name of the operation. """ - headers: Mapping[str, str] = field(default_factory=dict, kw_only=True) + headers: Mapping[str, str] = field(default_factory=dict) """ Optional header fields sent by the caller. """ - task_cancellation: OperationTaskCancellation = field(kw_only=True) + task_cancellation: OperationTaskCancellation """ Task cancellation information indicating that a running task should be interrupted. This is distinct from operation cancellation. """ - request_deadline: Optional[datetime] = field(default=None, kw_only=True) + request_deadline: Optional[datetime] = field(default=None) """ The deadline for the operation handler method. Note that this is the time by which the current _request_ should complete, not the _operation_'s deadline. """ -@dataclass(frozen=True) +@dataclass(frozen=True, kw_only=True) class StartOperationContext(OperationContext): """Context for the start method. Includes information from the request.""" - request_id: str = field(kw_only=True) + request_id: str """ Request ID that may be used by the handler to dedupe a start request. By default a v4 UUID should be generated by the client. """ - callback_url: Optional[str] = field(default=None, kw_only=True) + callback_url: Optional[str] = field(default=None) """ A callback URL is required to deliver the completion of an async operation. This URL should be called by a handler upon completion if the started operation is async. """ - callback_headers: Mapping[str, str] = field(default_factory=dict, kw_only=True) + callback_headers: Mapping[str, str] = field(default_factory=dict) """ Optional header fields set by the caller to be attached to the callback request when an asynchronous operation completes. """ - inbound_links: Sequence[Link] = field(default_factory=list, kw_only=True) + inbound_links: Sequence[Link] = field(default_factory=list) """ Links received in the request. This list is automatically populated when handling a start request. Handlers may use these links, for example to add information about the caller to a resource associated with the operation execution. """ - outbound_links: list[Link] = field(default_factory=list, kw_only=True) + outbound_links: list[Link] = field(default_factory=list) """ Links to be returned by the handler. This list is initially empty. Handlers may populate this list, for example with links describing resources associated with the @@ -120,7 +120,7 @@ class StartOperationContext(OperationContext): """ -@dataclass(frozen=True) +@dataclass(frozen=True, kw_only=True) class CancelOperationContext(OperationContext): """Context for the cancel method. From 1a87045afbda8bd0839007b38a15ef28c31e3106 Mon Sep 17 00:00:00 2001 From: James Watkins-Harvey Date: Tue, 2 Dec 2025 13:16:13 -0500 Subject: [PATCH 5/6] Address review comments --- src/nexusrpc/handler/_common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nexusrpc/handler/_common.py b/src/nexusrpc/handler/_common.py index 48a88ca..0e53704 100644 --- a/src/nexusrpc/handler/_common.py +++ b/src/nexusrpc/handler/_common.py @@ -74,7 +74,7 @@ def __new__(cls, *args: Any, **kwargs: Any): Task cancellation information indicating that a running task should be interrupted. This is distinct from operation cancellation. """ - request_deadline: Optional[datetime] = field(default=None) + request_deadline: Optional[datetime] = None """ The deadline for the operation handler method. Note that this is the time by which the current _request_ should complete, not the _operation_'s deadline. @@ -93,7 +93,7 @@ class StartOperationContext(OperationContext): By default a v4 UUID should be generated by the client. """ - callback_url: Optional[str] = field(default=None) + callback_url: Optional[str] = None """ A callback URL is required to deliver the completion of an async operation. This URL should be called by a handler upon completion if the started operation is async. From 9b94ad53cb3415b337a4321d0205abbe1cd15c3c Mon Sep 17 00:00:00 2001 From: James Watkins-Harvey Date: Thu, 4 Dec 2025 18:04:41 -0500 Subject: [PATCH 6/6] Address review comments --- src/nexusrpc/handler/_common.py | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/nexusrpc/handler/_common.py b/src/nexusrpc/handler/_common.py index 0e53704..1b17910 100644 --- a/src/nexusrpc/handler/_common.py +++ b/src/nexusrpc/handler/_common.py @@ -22,17 +22,23 @@ class OperationTaskCancellation(ABC): @abstractmethod def is_cancelled(self) -> bool: - """Return True if the associated task has been cancelled.""" + """ + Return True if the associated task has been cancelled. + """ raise NotImplementedError @abstractmethod def cancellation_reason(self) -> Optional[str]: - """Provide additional context for the cancellation, if available.""" + """ + Provide additional context for the cancellation, if available. + """ raise NotImplementedError @abstractmethod def wait_until_cancelled_sync(self, timeout: Optional[float] = None) -> bool: - """Block until cancellation occurs or the optional timeout elapses. Nexus worker implementations may return `True` for :py:attr:`is_cancelled` before this method returns and therefore may cause a race condition if both are used in tandem.""" + """ + Block until cancellation occurs or the optional timeout elapses. Nexus worker implementations may return `True` for :py:attr:`is_cancelled` before this method returns and therefore may cause a race condition if both are used in tandem. + """ raise NotImplementedError @abstractmethod @@ -43,9 +49,11 @@ async def wait_until_cancelled(self) -> None: @dataclass(frozen=True, kw_only=True) class OperationContext(ABC): - """Context for the execution of the requested operation method. + """ + Context for the execution of the requested operation method. - Includes information from the request.""" + Includes information from the request. + """ def __new__(cls, *args: Any, **kwargs: Any): if cls is OperationContext: @@ -64,7 +72,7 @@ def __new__(cls, *args: Any, **kwargs: Any): The name of the operation. """ - headers: Mapping[str, str] = field(default_factory=dict) + headers: Mapping[str, str] """ Optional header fields sent by the caller. """ @@ -83,9 +91,11 @@ def __new__(cls, *args: Any, **kwargs: Any): @dataclass(frozen=True, kw_only=True) class StartOperationContext(OperationContext): - """Context for the start method. + """ + Context for the start method. - Includes information from the request.""" + Includes information from the request. + """ request_id: str """ @@ -122,9 +132,11 @@ class StartOperationContext(OperationContext): @dataclass(frozen=True, kw_only=True) class CancelOperationContext(OperationContext): - """Context for the cancel method. + """ + Context for the cancel method. - Includes information from the request.""" + Includes information from the request. + """ @dataclass(frozen=True)