From c6241d290a4bca5119e22d55cc93114e0595a035 Mon Sep 17 00:00:00 2001 From: slinkydeveloper Date: Thu, 16 Jul 2026 15:08:55 +0200 Subject: [PATCH] Bump shared core to 7.0.1, add support for scope in virtual objects --- Cargo.lock | 4 +-- Cargo.toml | 2 +- python/restate/client.py | 44 +++++++++++++++++++++++++++++++ python/restate/client_types.py | 37 ++++++++++++++++++++++++++ python/restate/context.py | 35 +++++++++++++++++++++++++ python/restate/server_context.py | 45 ++++++++++++++++++++++++++++++++ 6 files changed, 164 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7ffd532..d1b72fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -814,9 +814,9 @@ dependencies = [ [[package]] name = "restate-sdk-shared-core" -version = "7.0.0" +version = "7.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bf02583379fc28d6386bd864629ba6553ce942124956b862fa9e95a4d836558" +checksum = "8bd1f1d1961ac32aea52923f089e668a07630ca9c4edcadf18d38bc24e5f14c0" dependencies = [ "base64", "bs58", diff --git a/Cargo.toml b/Cargo.toml index 4804345..38d9cfb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,4 +14,4 @@ doc = false [dependencies] pyo3 = { version = "0.25.1", features = ["extension-module"] } tracing-subscriber = { version = "0.3", features = ["fmt", "env-filter"] } -restate-sdk-shared-core = { version = "7.0.0", features = ["request_identity", "sha2_random_seed", "rust_crypto"] } +restate-sdk-shared-core = { version = "7.0.1", features = ["request_identity", "sha2_random_seed", "rust_crypto"] } diff --git a/python/restate/client.py b/python/restate/client.py index f78c336..dc39213 100644 --- a/python/restate/client.py +++ b/python/restate/client.py @@ -380,6 +380,50 @@ async def service_send( send = typing.cast(typing.Dict[str, str], send_handle) return RestateClientSendHandle(send.get("invocationId", ""), 200) + async def object_call( + self, + tpe: HandlerType[I, O], + key: str, + arg: I, + limit_key: str | None = None, + idempotency_key: str | None = None, + headers: typing.Dict[str, str] | None = None, + ) -> O: + return await self.client.do_call( + tpe, + arg, + key, + idempotency_key=idempotency_key, + headers=headers, + scope=self.scope_key, + limit_key=limit_key, + ) + + async def object_send( + self, + tpe: HandlerType[I, O], + key: str, + arg: I, + send_delay: typing.Optional[timedelta] = None, + limit_key: str | None = None, + idempotency_key: str | None = None, + headers: typing.Dict[str, str] | None = None, + ) -> RestateClientSendHandle: + send_handle = await self.client.do_call( + tpe, + parameter=arg, + key=key, + send=True, + send_delay=send_delay, + idempotency_key=idempotency_key, + headers=headers, + force_json_output=True, + scope=self.scope_key, + limit_key=limit_key, + ) + send = typing.cast(typing.Dict[str, str], send_handle) + return RestateClientSendHandle(send.get("invocationId", ""), 200) + async def workflow_call( self, tpe: HandlerType[I, O], diff --git a/python/restate/client_types.py b/python/restate/client_types.py index 4613fc5..5908f7e 100644 --- a/python/restate/client_types.py +++ b/python/restate/client_types.py @@ -94,6 +94,43 @@ async def service_send( """Make a send operation to the given handler, within this scope""" pass + @abc.abstractmethod + async def object_call( + self, + tpe: HandlerType[I, O], + key: str, + arg: I, + limit_key: str | None = None, + idempotency_key: str | None = None, + headers: typing.Dict[str, str] | None = None, + ) -> O: + """ + Make an RPC call to the given object handler, within this scope. + + NOTE: To use scopes with virtual objects you must enable the additional experimental feature in restate-server, + via RESTATE_EXPERIMENTAL_ENABLE_SCOPED_VIRTUAL_OBJECTS=true + """ + pass + + @abc.abstractmethod + async def object_send( + self, + tpe: HandlerType[I, O], + key: str, + arg: I, + send_delay: typing.Optional[timedelta] = None, + limit_key: str | None = None, + idempotency_key: str | None = None, + headers: typing.Dict[str, str] | None = None, + ) -> RestateClientSendHandle: + """ + Make a send operation to the given object handler, within this scope. + + NOTE: To use scopes with virtual objects you must enable the additional experimental feature in restate-server, + via RESTATE_EXPERIMENTAL_ENABLE_SCOPED_VIRTUAL_OBJECTS=true + """ + pass + @abc.abstractmethod async def workflow_call( self, diff --git a/python/restate/context.py b/python/restate/context.py index 642366f..feb0086 100644 --- a/python/restate/context.py +++ b/python/restate/context.py @@ -281,6 +281,41 @@ def service_send( Invokes the given service with the given argument, within this scope. """ + @abc.abstractmethod + def object_call( + self, + tpe: HandlerType[I, O], + key: str, + arg: I, + limit_key: str | None = None, + idempotency_key: str | None = None, + headers: typing.Dict[str, str] | None = None, + ) -> RestateDurableCallFuture[O]: + """ + Invokes the given object handler with the given argument, within this scope. + + NOTE: To use scopes with virtual objects you must enable the additional experimental feature in restate-server, + via RESTATE_EXPERIMENTAL_ENABLE_SCOPED_VIRTUAL_OBJECTS=true + """ + + @abc.abstractmethod + def object_send( + self, + tpe: HandlerType[I, O], + key: str, + arg: I, + send_delay: Optional[timedelta] = None, + limit_key: str | None = None, + idempotency_key: str | None = None, + headers: typing.Dict[str, str] | None = None, + ) -> SendHandle: + """ + Invokes the given object handler with the given argument, within this scope. + + NOTE: To use scopes with virtual objects you must enable the additional experimental feature in restate-server, + via RESTATE_EXPERIMENTAL_ENABLE_SCOPED_VIRTUAL_OBJECTS=true + """ + @abc.abstractmethod def workflow_call( self, diff --git a/python/restate/server_context.py b/python/restate/server_context.py index cbaf4ea..4b1b60b 100644 --- a/python/restate/server_context.py +++ b/python/restate/server_context.py @@ -260,6 +260,51 @@ def service_send( assert isinstance(send, SendHandle) return send + def object_call( + self, + tpe: HandlerType[I, O], + key: str, + arg: I, + limit_key: str | None = None, + idempotency_key: str | None = None, + headers: typing.Dict[str, str] | None = None, + ) -> RestateDurableCallFuture[O]: + coro = self.context.do_call( + tpe, + arg, + key, + idempotency_key=idempotency_key, + headers=headers, + scope=self.scope, + limit_key=limit_key, + ) + assert not isinstance(coro, SendHandle) + return coro + + def object_send( + self, + tpe: HandlerType[I, O], + key: str, + arg: I, + send_delay: Optional[timedelta] = None, + limit_key: str | None = None, + idempotency_key: str | None = None, + headers: typing.Dict[str, str] | None = None, + ) -> SendHandle: + send = self.context.do_call( + tpe=tpe, + key=key, + parameter=arg, + send_delay=send_delay, + send=True, + idempotency_key=idempotency_key, + headers=headers, + scope=self.scope, + limit_key=limit_key, + ) + assert isinstance(send, SendHandle) + return send + def workflow_call( self, tpe: HandlerType[I, O],