fixt: Add runScriptByPath and runScriptByHash methods to SDK clients (#6252)

* feat: Add runScriptByPath and runScriptByHash methods to SDK clients

- Add runScriptByPath and runScriptByHash methods to TypeScript client
- Add run_script_by_path and run_script_by_hash methods to Python client
- Split functionality from existing methods that took both path and hash parameters
- Add deprecation warnings to existing run_script methods
- Maintain backward compatibility while encouraging use of focused methods

Closes #6251

Co-authored-by: Ruben Fiszel <rubenfiszel@users.noreply.github.com>

* Refactor SDK methods to eliminate code duplication

- Extract common logic into internal helper methods
- Python client: _run_script_async_internal() and _run_script_internal()
- TypeScript client: _runScriptAsyncInternal() and _runScriptInternal()
- Eliminate duplicated parameter processing and HTTP setup
- Maintain exact same public API surface and functionality
- Reduce lines of code while preserving all existing behaviors

Co-authored-by: Ruben Fiszel <rubenfiszel@users.noreply.github.com>

* fix ts and dev.nu for python

* trade warnings.warn for logging.warning

Signed-off-by: pyranota <pyra@duck.com>

---------

Signed-off-by: pyranota <pyra@duck.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Ruben Fiszel <rubenfiszel@users.noreply.github.com>
Co-authored-by: pyranota <pyra@duck.com>
This commit is contained in:
claude[bot]
2025-07-22 12:21:21 +00:00
committed by GitHub
parent 1713987317
commit 8bc2a5733e
6 changed files with 231 additions and 14 deletions

View File

@@ -127,6 +127,7 @@
# Other languages
deno
typescript
nushell
go
bun

View File

@@ -1,6 +1,6 @@
#! /usr/bin/env nu
let cache = "/tmp/windmill/cache/python_311/"
let cache = "/tmp/windmill/cache/python_3_11/"
# Clean cache
def "main clean" [] {
@@ -42,7 +42,7 @@ def main [
rm -rf ($cache ++ wmill*/wmill/*)
# Copy files from local ./dist to every wm-client version in cache
ls /tmp/windmill/cache/python_311/wmill* | each {
ls /tmp/windmill/cache/python_3_11/wmill* | each {
|i|
let path = $i | get name;

View File

@@ -120,22 +120,58 @@ class Windmill:
args: dict = None,
scheduled_in_secs: int = None,
) -> str:
"""Create a script job and return its job id."""
"""Create a script job and return its job id.
.. deprecated:: Use run_script_by_path_async or run_script_by_hash_async instead.
"""
logging.warning(
"run_script_async is deprecated. Use run_script_by_path_async or run_script_by_hash_async instead.",
)
assert not (path and hash_), "path and hash_ are mutually exclusive"
return self._run_script_async_internal(path=path, hash_=hash_, args=args, scheduled_in_secs=scheduled_in_secs)
def _run_script_async_internal(
self,
path: str = None,
hash_: str = None,
args: dict = None,
scheduled_in_secs: int = None,
) -> str:
"""Internal helper for running scripts asynchronously."""
args = args or {}
params = {"scheduled_in_secs": scheduled_in_secs} if scheduled_in_secs else {}
if os.environ.get("WM_JOB_ID"):
params["parent_job"] = os.environ.get("WM_JOB_ID")
if os.environ.get("WM_ROOT_FLOW_JOB_ID"):
params["root_job"] = os.environ.get("WM_ROOT_FLOW_JOB_ID")
if path:
endpoint = f"/w/{self.workspace}/jobs/run/p/{path}"
elif hash_:
endpoint = f"/w/{self.workspace}/jobs/run/h/{hash_}"
else:
raise Exception("path or hash_ must be provided")
return self.post(endpoint, json=args, params=params).text
def run_script_by_path_async(
self,
path: str,
args: dict = None,
scheduled_in_secs: int = None,
) -> str:
"""Create a script job by path and return its job id."""
return self._run_script_async_internal(path=path, args=args, scheduled_in_secs=scheduled_in_secs)
def run_script_by_hash_async(
self,
hash_: str,
args: dict = None,
scheduled_in_secs: int = None,
) -> str:
"""Create a script job by hash and return its job id."""
return self._run_script_async_internal(hash_=hash_, args=args, scheduled_in_secs=scheduled_in_secs)
def run_flow_async(
self,
path: str,
@@ -170,20 +206,76 @@ class Windmill:
cleanup: bool = True,
assert_result_is_not_none: bool = False,
) -> Any:
"""Run script synchronously and return its result."""
"""Run script synchronously and return its result.
.. deprecated:: Use run_script_by_path or run_script_by_hash instead.
"""
logging.warning(
"run_script is deprecated. Use run_script_by_path or run_script_by_hash instead.",
)
assert not (path and hash_), "path and hash_ are mutually exclusive"
return self._run_script_internal(
path=path, hash_=hash_, args=args, timeout=timeout, verbose=verbose,
cleanup=cleanup, assert_result_is_not_none=assert_result_is_not_none
)
def _run_script_internal(
self,
path: str = None,
hash_: str = None,
args: dict = None,
timeout: dt.timedelta | int | float | None = None,
verbose: bool = False,
cleanup: bool = True,
assert_result_is_not_none: bool = False,
) -> Any:
"""Internal helper for running scripts synchronously."""
args = args or {}
if verbose:
logger.info(f"running `{path}` synchronously with {args = }")
if path:
logger.info(f"running `{path}` synchronously with {args = }")
elif hash_:
logger.info(f"running script with hash `{hash_}` synchronously with {args = }")
if isinstance(timeout, dt.timedelta):
timeout = timeout.total_seconds()
job_id = self.run_script_async(path=path, hash_=hash_, args=args)
job_id = self._run_script_async_internal(path=path, hash_=hash_, args=args)
return self.wait_job(
job_id, timeout, verbose, cleanup, assert_result_is_not_none
)
def run_script_by_path(
self,
path: str,
args: dict = None,
timeout: dt.timedelta | int | float | None = None,
verbose: bool = False,
cleanup: bool = True,
assert_result_is_not_none: bool = False,
) -> Any:
"""Run script by path synchronously and return its result."""
return self._run_script_internal(
path=path, args=args, timeout=timeout, verbose=verbose,
cleanup=cleanup, assert_result_is_not_none=assert_result_is_not_none
)
def run_script_by_hash(
self,
hash_: str,
args: dict = None,
timeout: dt.timedelta | int | float | None = None,
verbose: bool = False,
cleanup: bool = True,
assert_result_is_not_none: bool = False,
) -> Any:
"""Run script by hash synchronously and return its result."""
return self._run_script_internal(
hash_=hash_, args=args, timeout=timeout, verbose=verbose,
cleanup=cleanup, assert_result_is_not_none=assert_result_is_not_none
)
def wait_job(
self,
job_id,
@@ -964,13 +1056,26 @@ def run_script_by_path_async(
args: Dict[str, Any] = None,
scheduled_in_secs: Union[None, int] = None,
) -> str:
return _client.run_script_async(
return _client.run_script_by_path_async(
path=path,
args=args,
scheduled_in_secs=scheduled_in_secs,
)
@init_global_client
def run_script_by_hash_async(
hash_: str,
args: Dict[str, Any] = None,
scheduled_in_secs: Union[None, int] = None,
) -> str:
return _client.run_script_by_hash_async(
hash_=hash_,
args=args,
scheduled_in_secs=scheduled_in_secs,
)
@init_global_client
def run_script_by_path_sync(
path: str,
@@ -1274,7 +1379,10 @@ def run_script(
cleanup: bool = True,
assert_result_is_not_none: bool = True,
) -> Any:
"""Run script synchronously and return its result."""
"""Run script synchronously and return its result.
.. deprecated:: Use run_script_by_path or run_script_by_hash instead.
"""
return _client.run_script(
path=path,
hash_=hash_,
@@ -1286,6 +1394,46 @@ def run_script(
)
@init_global_client
def run_script_by_path(
path: str,
args: dict = None,
timeout: dt.timedelta | int | float = None,
verbose: bool = False,
cleanup: bool = True,
assert_result_is_not_none: bool = True,
) -> Any:
"""Run script by path synchronously and return its result."""
return _client.run_script_by_path(
path=path,
args=args,
verbose=verbose,
assert_result_is_not_none=assert_result_is_not_none,
cleanup=cleanup,
timeout=timeout,
)
@init_global_client
def run_script_by_hash(
hash_: str,
args: dict = None,
timeout: dt.timedelta | int | float = None,
verbose: bool = False,
cleanup: bool = True,
assert_result_is_not_none: bool = True,
) -> Any:
"""Run script by hash synchronously and return its result."""
return _client.run_script_by_hash(
hash_=hash_,
args=args,
verbose=verbose,
assert_result_is_not_none=assert_result_is_not_none,
cleanup=cleanup,
timeout=timeout,
)
@init_global_client
def username_to_email(username: str) -> str:
"""
@@ -1374,4 +1522,4 @@ def parse_variable_syntax(s: str) -> Optional[str]:
"""Parse variable syntax from string."""
if s.startswith("var://"):
return s[6:]
return None
return None

View File

@@ -14,5 +14,6 @@ cp "${script_dirpath}/s3Types.ts" "${script_dirpath}/src/"
echo "" >> "${script_dirpath}/src/index.ts"
echo 'export type { S3Object, DenoS3LightClientSettings } from "./s3Types";' >> "${script_dirpath}/src/index.ts"
echo "" >> "${script_dirpath}/src/index.ts"
echo 'export { type Base64, setClient, getVariable, setVariable, getResource, setResource, getResumeUrls, setState, getState, getIdToken, denoS3LightClientSettings, loadS3FileStream, loadS3File, writeS3File, signS3Objects, signS3Object, task, runScript, runScriptAsync, runFlow, runFlowAsync, waitJob, getRootJobId, setFlowUserState, getFlowUserState, usernameToEmail, requestInteractiveSlackApproval, Sql, requestInteractiveTeamsApproval, } from "./client";' >> "${script_dirpath}/src/index.ts"
echo 'export { type Base64, setClient, getVariable, setVariable, getResource, setResource, getResumeUrls, setState, setProgress, getProgress, getState, getIdToken, denoS3LightClientSettings, loadS3FileStream, loadS3File, writeS3File, signS3Objects, signS3Object, task, runScript, runScriptAsync, runScriptByPath, runScriptByHash, runScriptByPathAsync, runScriptByHashAsync, runFlow, runFlowAsync, waitJob, getRootJobId, setFlowUserState, getFlowUserState, usernameToEmail, requestInteractiveSlackApproval, Sql, requestInteractiveTeamsApproval } from "./client";' >> "${script_dirpath}/src/index.ts"

View File

@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
set -eou pipefail
script_dirpath="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@@ -39,4 +39,4 @@ cp "${script_dirpath}/s3Types.ts" "${script_dirpath}/src/"
echo "" >> "${script_dirpath}/src/index.ts"
echo 'export type { S3Object, DenoS3LightClientSettings } from "./s3Types";' >> "${script_dirpath}/src/index.ts"
echo "" >> "${script_dirpath}/src/index.ts"
echo 'export { type Base64, setClient, getVariable, setVariable, getResource, setResource, getResumeUrls, setState, setProgress, getProgress, getState, getIdToken, denoS3LightClientSettings, loadS3FileStream, loadS3File, writeS3File, signS3Objects, signS3Object, task, runScript, runScriptAsync, runFlow, runFlowAsync, waitJob, getRootJobId, setFlowUserState, getFlowUserState, usernameToEmail, requestInteractiveSlackApproval, Sql, requestInteractiveTeamsApproval } from "./client";' >> "${script_dirpath}/src/index.ts"
echo 'export { type Base64, setClient, getVariable, setVariable, getResource, setResource, getResumeUrls, setState, setProgress, getProgress, getState, getIdToken, denoS3LightClientSettings, loadS3FileStream, loadS3File, writeS3File, signS3Objects, signS3Object, task, runScript, runScriptAsync, runScriptByPath, runScriptByHash, runScriptByPathAsync, runScriptByHashAsync, runFlow, runFlowAsync, waitJob, getRootJobId, setFlowUserState, getFlowUserState, usernameToEmail, requestInteractiveSlackApproval, Sql, requestInteractiveTeamsApproval } from "./client";' >> "${script_dirpath}/src/index.ts"

View File

@@ -129,22 +129,58 @@ export async function getRootJobId(jobId?: string): Promise<string> {
return await JobService.getRootJobId({ workspace, id: jobId });
}
/**
* @deprecated Use runScriptByPath or runScriptByHash instead
*/
export async function runScript(
path: string | null = null,
hash_: string | null = null,
args: Record<string, any> | null = null,
verbose: boolean = false
): Promise<any> {
console.warn('runScript is deprecated. Use runScriptByPath or runScriptByHash instead.');
if (path && hash_) {
throw new Error("path and hash_ are mutually exclusive");
}
return _runScriptInternal(path, hash_, args, verbose);
}
async function _runScriptInternal(
path: string | null = null,
hash_: string | null = null,
args: Record<string, any> | null = null,
verbose: boolean = false
): Promise<any> {
args = args || {};
if (verbose) {
console.info(`running \`${path}\` synchronously with args:`, args);
if (path) {
console.info(`running \`${path}\` synchronously with args:`, args);
} else if (hash_) {
console.info(`running script with hash \`${hash_}\` synchronously with args:`, args);
}
}
const jobId = await runScriptAsync(path, hash_, args);
const jobId = await _runScriptAsyncInternal(path, hash_, args);
return await waitJob(jobId, verbose);
}
export async function runScriptByPath(
path: string,
args: Record<string, any> | null = null,
verbose: boolean = false
): Promise<any> {
return _runScriptInternal(path, null, args, verbose);
}
export async function runScriptByHash(
hash_: string,
args: Record<string, any> | null = null,
verbose: boolean = false
): Promise<any> {
return _runScriptInternal(null, hash_, args, verbose);
}
export async function runFlow(
path: string | null = null,
args: Record<string, any> | null = null,
@@ -243,16 +279,30 @@ export function task<P, T>(f: (_: P) => T): (_: P) => Promise<T> {
};
}
/**
* @deprecated Use runScriptByPathAsync or runScriptByHashAsync instead
*/
export async function runScriptAsync(
path: string | null,
hash_: string | null,
args: Record<string, any> | null,
scheduledInSeconds: number | null = null
): Promise<string> {
console.warn('runScriptAsync is deprecated. Use runScriptByPathAsync or runScriptByHashAsync instead.');
// Create a script job and return its job id.
if (path && hash_) {
throw new Error("path and hash_ are mutually exclusive");
}
return _runScriptAsyncInternal(path, hash_, args, scheduledInSeconds);
}
async function _runScriptAsyncInternal(
path: string | null = null,
hash_: string | null = null,
args: Record<string, any> | null = null,
scheduledInSeconds: number | null = null
): Promise<string> {
// Create a script job and return its job id.
args = args || {};
const params: Record<string, any> = {};
@@ -278,6 +328,7 @@ export async function runScriptAsync(
} else {
throw new Error("path or hash_ must be provided");
}
let url = new URL(OpenAPI.BASE + endpoint);
url.search = new URLSearchParams(params).toString();
@@ -291,6 +342,22 @@ export async function runScriptAsync(
}).then((res) => res.text());
}
export async function runScriptByPathAsync(
path: string,
args: Record<string, any> | null = null,
scheduledInSeconds: number | null = null
): Promise<string> {
return _runScriptAsyncInternal(path, null, args, scheduledInSeconds);
}
export async function runScriptByHashAsync(
hash_: string,
args: Record<string, any> | null = null,
scheduledInSeconds: number | null = null
): Promise<string> {
return _runScriptAsyncInternal(null, hash_, args, scheduledInSeconds);
}
export async function runFlowAsync(
path: string | null,
args: Record<string, any> | null,