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
+2 -2
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;
+155 -7
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