diff --git a/python-client/wmill/README.md b/python-client/wmill/README.md index 2bb1066fc4..75bac013d0 100644 --- a/python-client/wmill/README.md +++ b/python-client/wmill/README.md @@ -3,22 +3,81 @@ The core client for the [Windmill](https://windmill.dev) platform. -## Quickstart +## Usage + +### Basic Usage + +The `wmill` package has several methods at the top-level for the most frequent operations you will need. + +The following are some common examples: ```python +import time + import wmill def main(): - client = wmill.Windmill( + # Get the value of a variable + wmill.get_variable("u/user/variable_path") + + # Run a script synchronously and get the result + wmill.run_script("f/pathto/script", args={"arg1": "value1"}) + + # Get the value of a resource + wmill.get_resource("u/user/resource_path") + + # Set the script's state + wmill.set_state({"ts": time.time()}) + + # Get the script's state + wmill.get_state() +``` + +### Advanced Usage + +The `wmill` package also exposes the `Windmill` class, which is the core client for the Windmill platform. + +```python +import time + +from wmill import Windmill + +def main(): + client = Windmill( # token=... <- this is optional. otherwise the client will look for the WM_TOKEN env var ) - print(client.version) - print(client.get("u/user/resource_path")) + # Get the current version of the client + client.version + # Get the current user + client.user + + # Convenience get and post methods exist for https://app.windmill.dev/openapi.html#/ + # these are thin wrappers around the httpx library's get and post methods + # list worker groups + client.get("/configs/list_worker_groups") + # create a group + client.post( + f"/w/{client.workspace}/groups/create", + json={ + "name": "my-group", + "summary": "my group summary", + } + ) + + # Get and set the state of the script + now = time.time() + client.state = {"ts": now} + assert client.state == {"ts": now} + + # Run a job asynchronously job_id = client.run_script_async(path="path/to/script") - print(job_id) + # Get its status + client.get_job_status(job_id) + # Get its result + client.get_result(job_id) + - return client.run_script(path="path/to/script", args={"arg1": "value1"}) ``` diff --git a/python-client/wmill/wmill/client.py b/python-client/wmill/wmill/client.py index 7dcd69f3ec..fea2a7fa7a 100644 --- a/python-client/wmill/wmill/client.py +++ b/python-client/wmill/wmill/client.py @@ -228,7 +228,9 @@ class Windmill: def set_variable(self, path: str, value: str) -> None: """Set variable from Windmill""" # check if variable exists - r = self.get(f"/w/{self.workspace}/variables/get/{path}", raise_for_status=False) + r = self.get( + f"/w/{self.workspace}/variables/get/{path}", raise_for_status=False + ) if r.status_code == 404: # create variable self.post( @@ -254,7 +256,9 @@ class Windmill: ) -> str | None: """Get resource from Windmill""" try: - return self.get(f"/w/{self.workspace}/resources/get_value_interpolated/{path}").json() + return self.get( + f"/w/{self.workspace}/resources/get_value_interpolated/{path}" + ).json() except Exception as e: if none_if_undefined: return None @@ -268,7 +272,9 @@ class Windmill: resource_type: str, ): # check if resource exists - r = self.get(f"/w/{self.workspace}/resources/get/{path}", raise_for_status=False) + r = self.get( + f"/w/{self.workspace}/resources/get/{path}", raise_for_status=False + ) if r.status_code == 404: # create resource self.post( @@ -355,6 +361,10 @@ class Windmill: def state(self) -> Any: return self.get_resource(path=self.state_path, none_if_undefined=True) + @state.setter + def state(self, value: Any) -> None: + self.set_state(value) + @staticmethod def set_shared_state_pickle(value: Any, path: str = "state.pickle") -> None: """ @@ -631,3 +641,31 @@ def get_state_path() -> str: @init_global_client def get_resume_urls(approver: str = None) -> dict: return _client.get_resume_urls(approver) + + +@init_global_client +def cancel_running() -> dict: + """Cancel currently running executions of the same script.""" + return _client.cancel_running() + + +@init_global_client +def run_script( + path: str = None, + hash_: str = None, + 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 synchronously and return its result.""" + return _client.run_script( + path=path, + hash_=hash_, + args=args, + verbose=verbose, + assert_result_is_not_none=assert_result_is_not_none, + cleanup=cleanup, + timeout=timeout, + )