From 9f19d915965ebb288ccd9abad6d1bc2195c553ff Mon Sep 17 00:00:00 2001 From: Bryan Date: Wed, 7 Jan 2026 11:38:25 +0800 Subject: [PATCH] feat(sdk): support override paths in get_state/set_state functions (#7473) Co-authored-by: hugocasa --- go-client/windmill.go | 18 +++++++++++++---- python-client/wmill/wmill/client.py | 25 ++++++++++++++++------- rust-client/src/client.rs | 31 +++++++++++++++++++++++++++++ typescript-client/client.ts | 10 ++++++---- 4 files changed, 69 insertions(+), 15 deletions(-) diff --git a/go-client/windmill.go b/go-client/windmill.go index d64128a91a..7d7c1e70de 100644 --- a/go-client/windmill.go +++ b/go-client/windmill.go @@ -145,12 +145,22 @@ func GetStatePath() string { return value } -func GetState() (interface{}, error) { - return GetResource(GetStatePath()) +func getStatePathFromOpt(pathOpt ...string) string { + if len(pathOpt) > 0 { + path := pathOpt[0] + if len(path) > 0 { + return path + } + } + return GetStatePath() } -func SetState(state interface{}) error { - err := SetResource(GetStatePath(), state) +func GetState(pathOpt ...string) (interface{}, error) { + return GetResource(getStatePathFromOpt(pathOpt...)) +} + +func SetState(state interface{}, pathOpt ...string) error { + err := SetResource(getStatePathFromOpt(pathOpt...), state) if err != nil { return err } diff --git a/python-client/wmill/wmill/client.py b/python-client/wmill/wmill/client.py index 09877d8246..28f4da28c4 100644 --- a/python-client/wmill/wmill/client.py +++ b/python-client/wmill/wmill/client.py @@ -707,13 +707,25 @@ class Windmill: params=params if params else None, ).json() - def set_state(self, value: Any): + def set_state(self, value: Any, path: str | None = None) -> None: """Set the workflow state. Args: value: State value to set + path: Optional state resource path override. """ - self.set_resource(value, path=self.state_path, resource_type="state") + self.set_resource(value, path=path or self.state_path, resource_type="state") + + def get_state(self, path: str | None = None) -> Any: + """Get the workflow state. + + Args: + path: Optional state resource path override. + + Returns: + State value or None if not set + """ + return self.get_resource(path=path or self.state_path, none_if_undefined=True) def set_progress(self, value: int, job_id: Optional[str] = None): """Set job progress percentage (0-99). @@ -1725,12 +1737,11 @@ def whoami() -> dict: @init_global_client -@deprecate("Windmill().state") -def get_state() -> Any: +def get_state(path: str | None = None) -> Any: """ Get the state """ - return _client.state + return _client.get_state(path=path) @init_global_client @@ -1781,11 +1792,11 @@ def list_resources( @init_global_client -def set_state(value: Any) -> None: +def set_state(value: Any, path: str | None = None) -> None: """ Set the state """ - return _client.set_state(value) + return _client.set_state(value, path=path) @init_global_client diff --git a/rust-client/src/client.rs b/rust-client/src/client.rs index b3a2a674ec..61ec7a5d78 100644 --- a/rust-client/src/client.rs +++ b/rust-client/src/client.rs @@ -479,6 +479,17 @@ impl Windmill { ret!(self.get_resource_inner(&get_state_path()?)); } + /// Retrieves and deserializes the typed state value from a custom resource path. + /// + /// This is the same as [`Self::get_state`] but allows overriding the underlying state + /// resource path. Useful when you want to read/write a different state resource. + pub fn get_state_at<'a, T: serde::de::DeserializeOwned>( + &'a self, + path: &'a str, + ) -> MaybeFuture<'a, Result> { + ret!(self.get_resource_inner(path)); + } + /// Retrieves the current state value for a script's execution context. /// /// States persist data between runs of the same script by the same trigger (schedule or user). @@ -522,6 +533,14 @@ impl Windmill { ret!(self.get_resource_any_inner(&get_state_path()?)); } + /// Retrieves the untyped state value from a custom resource path. + /// + /// This is the same as [`Self::get_state_any`] but allows overriding the underlying state + /// resource path. + pub fn get_state_any_at<'a>(&'a self, path: &'a str) -> MaybeFuture<'a, Result> { + ret!(self.get_resource_any_inner(path)); + } + /// Updates or clears the script's persistent state. /// /// # Arguments @@ -549,6 +568,18 @@ impl Windmill { ret!(self.set_resource_inner(value, &get_state_path()?, "state")); } + /// Updates or clears a state resource at a custom path. + /// + /// This is the same as [`Self::set_state`] but allows overriding the target state resource + /// path. + pub fn set_state_at<'a>( + &'a self, + value: Option, + path: &'a str, + ) -> MaybeFuture<'a, Result<(), SdkError>> { + ret!(self.set_resource_inner(value, path, "state")); + } + /// Executes a script synchronously and waits for its completion. /// /// This is a blocking version of `run_script_async` that handles the entire script execution diff --git a/typescript-client/client.ts b/typescript-client/client.ts index e766f306b2..7f3b12baf0 100644 --- a/typescript-client/client.ts +++ b/typescript-client/client.ts @@ -589,9 +589,10 @@ export async function setInternalState(state: any): Promise { /** * Set the state * @param state state to set + * @param path Optional state resource path override. Defaults to `getStatePath()`. */ -export async function setState(state: any): Promise { - await setResource(state, undefined, "state"); +export async function setState(state: any, path?: string): Promise { + await setResource(state, path ?? getStatePath(), "state"); } /** @@ -706,9 +707,10 @@ export async function getInternalState(): Promise { /** * Get the state shared across executions + * @param path Optional state resource path override. Defaults to `getStatePath()`. */ -export async function getState(): Promise { - return await getResource(getStatePath(), true); +export async function getState(path?: string): Promise { + return await getResource(path ?? getStatePath(), true); } /**