From d97cdfde8965bfe4738561cab4b50e05cc0dafa3 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Wed, 25 Jun 2025 18:06:08 +0200 Subject: [PATCH 01/46] Added project setup --- examples/python_uv/project_setup.py | 141 ++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 examples/python_uv/project_setup.py diff --git a/examples/python_uv/project_setup.py b/examples/python_uv/project_setup.py new file mode 100644 index 000000000..7eda7cb15 --- /dev/null +++ b/examples/python_uv/project_setup.py @@ -0,0 +1,141 @@ +# Copyright (C) 2022 - 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import argparse +import logging +import os + +from ansys.hps.client import Client, HPSError +from ansys.hps.client.jms import ( + File, + JmsApi, + Job, + JobDefinition, + Project, + ProjectApi, + ResourceRequirements, + Software, + SuccessCriteria, + TaskDefinition, +) + +log = logging.getLogger(__name__) + + +def create_project(client, num_jobs): + log.debug("=== Create Project") + jms_api = JmsApi(client) + proj = jms_api.create_project( + Project( + name=f"Python UV example - {num_jobs} jobs", + priority=1, + active=True, + ), + replace=True, + ) + project_api = ProjectApi(client, proj.id) + + log.debug("=== Define Files") + cwd = os.path.dirname(__file__) + # Input Files + files = [ + File( + name="eval", + evaluation_path="eval.py", + type="text/plain", + src=os.path.join(cwd, "eval.py"), + ), + File( + name="exec_script", + evaluation_path="exec_script.py", + type="text/plain", + src=os.path.join(cwd, "exec_script.py"), + ), + File( + name="plot", + evaluation_path="plot.png", + type="image/png", + collect=True, + ), + ] + files = project_api.create_files(files) + file_ids = {f.name: f.id for f in files} + + log.debug("=== Define Task") + task_def = TaskDefinition( + name="plotting", + software_requirements=[Software(name="Uv")], + resource_requirements=ResourceRequirements( + num_cores=0.5, + memory=100 * 1024 * 1024, # 100 MB + disk_space=10 * 1024 * 1024, # 10 MB + ), + execution_level=0, + max_execution_time=500.0, + use_execution_script=True, + execution_script_id=file_ids["exec_script"], + execution_command="%executable% run %file:eval%", + input_file_ids=[file_ids["eval"]], + output_file_ids=[file_ids["plot"]], + success_criteria=SuccessCriteria( + return_code=0, + require_all_output_files=True, + ), + ) + task_defs = project_api.create_task_definitions([task_def]) + + print("== Define Job") + job_def = JobDefinition( + name="JobDefinition.1", active=True, task_definition_ids=[task_defs[0].id] + ) + job_def = project_api.create_job_definitions([job_def])[0] + log.debug(f"== Create {num_jobs} Jobs") + jobs = [] + for i in range(num_jobs): + jobs.append(Job(name=f"Job.{i}", eval_status="pending", job_definition_id=job_def.id)) + project_api.create_jobs(jobs) + log.info(f"Created project '{proj.name}', ID='{proj.id}'") + return proj + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("-j", "--num-jobs", type=int, default=10) + parser.add_argument("-U", "--url", default="https://localhost:8443/hps") + parser.add_argument("-u", "--username", default="repuser") + parser.add_argument("-p", "--password", default="repuser") + args = parser.parse_args() + + logger = logging.getLogger() + logging.basicConfig(format="%(message)s", level=logging.DEBUG) + + try: + log.info("Connect to HPC Platform Services") + client = Client(url=args.url, username=args.username, password=args.password) + log.info(f"HPS URL: {client.url}") + proj = create_project( + client=client, + num_jobs=args.num_jobs, + ) + + except HPSError as e: + log.error(str(e)) From 6b1742f432b0d42ec0663fbfb2dc99d105550884 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Wed, 25 Jun 2025 18:06:55 +0200 Subject: [PATCH 02/46] Add evaluation and execution scripts --- examples/python_uv/eval.py | 48 +++++++++++++++++ examples/python_uv/exec_script.py | 89 +++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 examples/python_uv/eval.py create mode 100644 examples/python_uv/exec_script.py diff --git a/examples/python_uv/eval.py b/examples/python_uv/eval.py new file mode 100644 index 000000000..46e265be2 --- /dev/null +++ b/examples/python_uv/eval.py @@ -0,0 +1,48 @@ +# Copyright (C) 2022 - 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# /// script +# requires-python = "3.12" +# dependencies = [ +# "numpy", +# "matplotlib" +# ] +# /// + +import json +import sys + +import matplotlib.pyplot as plt +import numpy as np + +if __name__ == "__main__": + # Generate plot + ts = np.linspace(0.0, 10.0, 100) + ys = np.sin(ts) + + fig, ax = plt.subplots() + ax.plot(ts, ys) + plt.savefig("plot.png", dpi=200) + + # Communicate location of venv for cleanup + with open("output_parameters.json", "w") as out_file: + json.dump({"exe": sys.executable}, out_file, indent=4) diff --git a/examples/python_uv/exec_script.py b/examples/python_uv/exec_script.py new file mode 100644 index 000000000..b09095741 --- /dev/null +++ b/examples/python_uv/exec_script.py @@ -0,0 +1,89 @@ +# Copyright (C) 2022 - 2025 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +"""Simplistic execution script for Python with Uv. + +Command formed: uv run +""" + +import json +import os +import shutil + +from ansys.rep.common.logging import log +from ansys.rep.evaluator.task_manager import ApplicationExecution + + +class PythonExecution(ApplicationExecution): + def execute(self): + log.info("Start Uv execution script") + + # Identify files + script_file = next((f for f in self.context.input_files if f["name"] == "eval"), None) + assert script_file, "Python script file missing" + output_filename = "output_parameters.json" + + # Identify applications + app_uv = next((a for a in self.context.software if a["name"] == "Uv"), None) + # app_xvfb = next((a for a in self.context.software if a["name"] == "Xvfb-run"), None) + assert app_uv, "Cannot find app Uv" + + # Add " around exe if needed for Windows + exes = {"uv": app_uv["executable"]} + for k, v in exes.items(): + if " " in v and not v.startswith('"'): + exes[k] = f'"{v}"' # noqa + + # Pass env vars correctly + env = dict(os.environ) + env.update(self.context.environment) + + ## Run evaluation script + cmd = f"{exes['uv']} run {script_file['path']}" + self.run_and_capture_output(cmd, shell=True, env=env) + + # Extract parameters if needed + try: + log.debug(f"Loading output parameters from {output_filename}") + with open(output_filename) as out_file: + output_parameters = json.load(out_file) + self.context.parameter_values.update(output_parameters) + log.debug(f"Loaded output parameters: {output_parameters}") + except Exception as ex: + log.info("No output parameters found.") + log.debug(f"Failed to read output_parameters from file: {ex}") + + # Clean up venv cache + if "exe" in output_parameters.keys(): + try: + venv_cache = os.path.abspath(os.path.join(output_parameters["exe"], "..", "..")) + venv_cache_parent = os.path.join(venv_cache, "..") + if os.path.exists(venv_cache): + log.debug(f"Current venv cache: {os.listdir(venv_cache_parent)}") + log.debug(f"Cleaning venv cache at {venv_cache}...") + shutil.rmtree(venv_cache) + else: + log.debug(f"Venv cache path {venv_cache} does not exist.") + except Exception as ex: + log.debug(f"Couldn't clean venv cache at {venv_cache}: {ex}") + + log.info("End Python execution script") From c6559d2c1e68a4c02b57115e3c9dff66b57efece Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Thu, 26 Jun 2025 09:00:37 +0200 Subject: [PATCH 03/46] Added README --- examples/python_uv/README.md | 92 ++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 examples/python_uv/README.md diff --git a/examples/python_uv/README.md b/examples/python_uv/README.md new file mode 100644 index 000000000..0b78ba6c7 --- /dev/null +++ b/examples/python_uv/README.md @@ -0,0 +1,92 @@ +# Running arbitrary python scripts on HPS +This example shows how arbitrary python scripts can be run on HPS, by using UV to generate +the required environments on the fly. + +The example sets up a project that will plot `sin(x)` using numpy and matplotlib, and then save +the figure to a file. After the run is completed, the generated ephemeral venv is cleaned up. + +# Prerequisites +In order for the example to run, `UV` must be installed and registered on the scaler/evaluator. +On a pcluster, this can be done in the following way: + +```bash +pip3 install --target=/ansys_inc/uv uv # Install UV via pip +mkdir -p /shared/rep_file_storage/uv/uv_cache # Create cache dir +``` + +Note that the directories `/ansys_inc/uv` and `/shared/rep_file_storage/uv/uv_cache` are shared +directories, they are both accessible by all evaluators. + +Next, the application must be registered in the scaler/evaluator with the following properties: + +| **Property** | **Value** | +|-------------------|---------------------------| +| Name | Uv | +| Version | 0.6.14 | +| Installation Path | /ansys_inc/uv | +| Executable | /ansys_inc/uv/bin/uv | + +and the following environment variable: + +| **Env Variable** | **Value** | +|------------------|--------------------------------------| +| UV_CACHE_DIR | /shared/rep_file_storage/uv/uv_cache | + +Note that the version should be adjusted to the case at hand. + +The above steps setup UV with the cache located in a shared folder, such that any dependency will +only need to be downloaded once. However, if the bandwidth between this shared folder and the +compute nodes is relatively slow, this can lead to long venv setuo times (on pclusters, a dozen +seconds is typical for reasonably large dependencies). + +Instead, UV can also be setup to use node local caching: + +## Node local cache +In cases where many short tasks are run, shorter runtimes can often be found by relocating the +UV cache to node local storage. In such a setup, each compute node has its own cache, meaning each +node will have to download all dependencies individually. Once the required packages are cached, +venv setup times will be on the order of 100 ms, 2 orders of magnitude faster than for the shared +case. To use node-local cache, the cache dir must be set to a directory local to each node. On +pclusters, for example, one could use the following value: + +| **Env Variable** | **Value** | +|------------------|--------------------------------------| +| UV_CACHE_DIR | /tmp/scratch/uv_cache | + +## Airgapped setups +For airgapped setups where no internet connectivity is available, there are several options for a +successful UV setup: + +1. Pre-populate the UV cache with all desired dependencies. +2. Provide a local python package index, and set UV to use it. More information can be found +[here](https://docs.astral.sh/uv/configuration/indexes/). This index could then sit in a shared +location, with node-local caching applied. +3. Use pre-generated virtual environments, see [here](https://docs.astral.sh/uv/reference/cli/#uv-venv) + +In order to disable network access, one can either set the `UV_OFFLINE` environment variable, or +use the `--offline` flag with many UV commands. + +# Running the example +To run the example, execute the `project_setup.py` script, for example via `uv run project_setup.py`. +This will setup a project with a number of jobs, and each job will generate a `plot.png` file. + + +## Options +The example supports the following command line arguments: + +| **Flag** | **Example** | **Description** | +|------------------------|----------------------------------|---------------------------------------------------------| +| -U, --url | --url=https://localhost:8443/hps |URL of the target HPS instance | +| -u, --username | --username=repuser |Username to log into HPS | +| -p, --password | --password=topSecret |Password to log into HPS | +| -j, --num-jobs | --num-jobs=10 |Number of jobs to generate | + +## Files +The relevant files of the example are: + +- `project_setup.py`: Handles all communication with the HPS instance. Defines the project and +generates the jobs. +- `eval.py`: The script that is evaluated on HPS. Contains the code to plot a sine, and then save +the figure. +- `exec_script.py`: Execution script that uses UV to run the evaluation script. Also cleans up the +venv generated by UV. \ No newline at end of file From fd2ac0d34a0fb7c789af16a90fc2e435cf7a8596 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Thu, 26 Jun 2025 09:01:11 +0200 Subject: [PATCH 04/46] Fixed UV header --- examples/python_uv/eval.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/python_uv/eval.py b/examples/python_uv/eval.py index 46e265be2..caaafaf5e 100644 --- a/examples/python_uv/eval.py +++ b/examples/python_uv/eval.py @@ -21,7 +21,7 @@ # SOFTWARE. # /// script -# requires-python = "3.12" +# requires-python = "==3.12" # dependencies = [ # "numpy", # "matplotlib" From ba06f36bd40e52cafef2c4075de095604218bf8e Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Thu, 26 Jun 2025 09:12:25 +0200 Subject: [PATCH 05/46] Added metadata header specification --- examples/python_uv/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/examples/python_uv/README.md b/examples/python_uv/README.md index 0b78ba6c7..5558f0b19 100644 --- a/examples/python_uv/README.md +++ b/examples/python_uv/README.md @@ -5,6 +5,21 @@ the required environments on the fly. The example sets up a project that will plot `sin(x)` using numpy and matplotlib, and then save the figure to a file. After the run is completed, the generated ephemeral venv is cleaned up. +The main feature enabling UV to take care of the environment setup is the metadata header present +in the `eval.py` script, which defines the dependencies: + +```python +# /// script +# requires-python = "==3.12" +# dependencies = [ +# "numpy", +# "matplotlib" +# ] +# /// +``` + +More information can be found [here (python.org)](https://packaging.python.org/en/latest/specifications/inline-script-metadata/#inline-script-metadata) and [here (astral.sh)](https://docs.astral.sh/uv/guides/scripts/#running-a-script-with-dependencies). + # Prerequisites In order for the example to run, `UV` must be installed and registered on the scaler/evaluator. On a pcluster, this can be done in the following way: From ef604bdbca27312848964977a5c4a4f3044279e8 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Thu, 26 Jun 2025 09:13:19 +0200 Subject: [PATCH 06/46] Better way to run --- examples/python_uv/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/python_uv/README.md b/examples/python_uv/README.md index 5558f0b19..26f371605 100644 --- a/examples/python_uv/README.md +++ b/examples/python_uv/README.md @@ -82,7 +82,7 @@ In order to disable network access, one can either set the `UV_OFFLINE` environm use the `--offline` flag with many UV commands. # Running the example -To run the example, execute the `project_setup.py` script, for example via `uv run project_setup.py`. +To run the example, execute the `project_setup.py` script, for example via `poetry run python project_setup.py`. This will setup a project with a number of jobs, and each job will generate a `plot.png` file. From 7199b9b4bf36e402ae47124a965043c13e06089d Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Thu, 26 Jun 2025 09:52:50 +0200 Subject: [PATCH 07/46] Added metadata header, run setup script via uv --- examples/python_uv/README.md | 2 +- examples/python_uv/project_setup.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/examples/python_uv/README.md b/examples/python_uv/README.md index 26f371605..5558f0b19 100644 --- a/examples/python_uv/README.md +++ b/examples/python_uv/README.md @@ -82,7 +82,7 @@ In order to disable network access, one can either set the `UV_OFFLINE` environm use the `--offline` flag with many UV commands. # Running the example -To run the example, execute the `project_setup.py` script, for example via `poetry run python project_setup.py`. +To run the example, execute the `project_setup.py` script, for example via `uv run project_setup.py`. This will setup a project with a number of jobs, and each job will generate a `plot.png` file. diff --git a/examples/python_uv/project_setup.py b/examples/python_uv/project_setup.py index 7eda7cb15..94848473e 100644 --- a/examples/python_uv/project_setup.py +++ b/examples/python_uv/project_setup.py @@ -20,6 +20,13 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +# /// script +# requires-python = "==3.10" +# dependencies = [ +# "ansys-hps-client @ git+https://github.com/ansys/pyhps.git@main" +# ] +# /// + import argparse import logging import os From 582949226cb6f84336521fcaafc03398c41bc914 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Thu, 26 Jun 2025 09:55:53 +0200 Subject: [PATCH 08/46] Added axis labels --- examples/python_uv/eval.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/python_uv/eval.py b/examples/python_uv/eval.py index caaafaf5e..9ba5ca749 100644 --- a/examples/python_uv/eval.py +++ b/examples/python_uv/eval.py @@ -41,6 +41,8 @@ fig, ax = plt.subplots() ax.plot(ts, ys) + ax.set_xlabel("Time [s]") + ax.set_ylabel("Displacement [cm]") plt.savefig("plot.png", dpi=200) # Communicate location of venv for cleanup From c5ddf346a29022d45117381fd61d07a4d330d309 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Mon, 30 Jun 2025 11:53:11 +0200 Subject: [PATCH 09/46] Replace UV/Uv with lowercase uv --- examples/python_uv/README.md | 28 ++++++++++++++-------------- examples/python_uv/exec_script.py | 8 ++++---- examples/python_uv/project_setup.py | 2 +- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/examples/python_uv/README.md b/examples/python_uv/README.md index 5558f0b19..d05f56765 100644 --- a/examples/python_uv/README.md +++ b/examples/python_uv/README.md @@ -1,11 +1,11 @@ # Running arbitrary python scripts on HPS -This example shows how arbitrary python scripts can be run on HPS, by using UV to generate +This example shows how arbitrary python scripts can be run on HPS, by using uv to generate the required environments on the fly. The example sets up a project that will plot `sin(x)` using numpy and matplotlib, and then save the figure to a file. After the run is completed, the generated ephemeral venv is cleaned up. -The main feature enabling UV to take care of the environment setup is the metadata header present +The main feature enabling uv to take care of the environment setup is the metadata header present in the `eval.py` script, which defines the dependencies: ```python @@ -21,11 +21,11 @@ in the `eval.py` script, which defines the dependencies: More information can be found [here (python.org)](https://packaging.python.org/en/latest/specifications/inline-script-metadata/#inline-script-metadata) and [here (astral.sh)](https://docs.astral.sh/uv/guides/scripts/#running-a-script-with-dependencies). # Prerequisites -In order for the example to run, `UV` must be installed and registered on the scaler/evaluator. +In order for the example to run, `uv` must be installed and registered on the scaler/evaluator. On a pcluster, this can be done in the following way: ```bash -pip3 install --target=/ansys_inc/uv uv # Install UV via pip +pip3 install --target=/ansys_inc/uv uv # Install uv via pip mkdir -p /shared/rep_file_storage/uv/uv_cache # Create cache dir ``` @@ -36,7 +36,7 @@ Next, the application must be registered in the scaler/evaluator with the follow | **Property** | **Value** | |-------------------|---------------------------| -| Name | Uv | +| Name | uv | | Version | 0.6.14 | | Installation Path | /ansys_inc/uv | | Executable | /ansys_inc/uv/bin/uv | @@ -49,16 +49,16 @@ and the following environment variable: Note that the version should be adjusted to the case at hand. -The above steps setup UV with the cache located in a shared folder, such that any dependency will +The above steps setup uv with the cache located in a shared folder, such that any dependency will only need to be downloaded once. However, if the bandwidth between this shared folder and the compute nodes is relatively slow, this can lead to long venv setuo times (on pclusters, a dozen seconds is typical for reasonably large dependencies). -Instead, UV can also be setup to use node local caching: +Instead, uv can also be setup to use node local caching: ## Node local cache In cases where many short tasks are run, shorter runtimes can often be found by relocating the -UV cache to node local storage. In such a setup, each compute node has its own cache, meaning each +uv cache to node local storage. In such a setup, each compute node has its own cache, meaning each node will have to download all dependencies individually. Once the required packages are cached, venv setup times will be on the order of 100 ms, 2 orders of magnitude faster than for the shared case. To use node-local cache, the cache dir must be set to a directory local to each node. On @@ -70,16 +70,16 @@ pclusters, for example, one could use the following value: ## Airgapped setups For airgapped setups where no internet connectivity is available, there are several options for a -successful UV setup: +successful uv setup: -1. Pre-populate the UV cache with all desired dependencies. -2. Provide a local python package index, and set UV to use it. More information can be found +1. Pre-populate the uv cache with all desired dependencies. +2. Provide a local python package index, and set uv to use it. More information can be found [here](https://docs.astral.sh/uv/configuration/indexes/). This index could then sit in a shared location, with node-local caching applied. 3. Use pre-generated virtual environments, see [here](https://docs.astral.sh/uv/reference/cli/#uv-venv) In order to disable network access, one can either set the `UV_OFFLINE` environment variable, or -use the `--offline` flag with many UV commands. +use the `--offline` flag with many uv commands. # Running the example To run the example, execute the `project_setup.py` script, for example via `uv run project_setup.py`. @@ -103,5 +103,5 @@ The relevant files of the example are: generates the jobs. - `eval.py`: The script that is evaluated on HPS. Contains the code to plot a sine, and then save the figure. -- `exec_script.py`: Execution script that uses UV to run the evaluation script. Also cleans up the -venv generated by UV. \ No newline at end of file +- `exec_script.py`: Execution script that uses uv to run the evaluation script. Also cleans up the +venv generated by uv. \ No newline at end of file diff --git a/examples/python_uv/exec_script.py b/examples/python_uv/exec_script.py index b09095741..ae4fbed60 100644 --- a/examples/python_uv/exec_script.py +++ b/examples/python_uv/exec_script.py @@ -20,7 +20,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -"""Simplistic execution script for Python with Uv. +"""Simplistic execution script for Python with uv. Command formed: uv run """ @@ -35,7 +35,7 @@ class PythonExecution(ApplicationExecution): def execute(self): - log.info("Start Uv execution script") + log.info("Start uv execution script") # Identify files script_file = next((f for f in self.context.input_files if f["name"] == "eval"), None) @@ -43,9 +43,9 @@ def execute(self): output_filename = "output_parameters.json" # Identify applications - app_uv = next((a for a in self.context.software if a["name"] == "Uv"), None) + app_uv = next((a for a in self.context.software if a["name"] == "uv"), None) # app_xvfb = next((a for a in self.context.software if a["name"] == "Xvfb-run"), None) - assert app_uv, "Cannot find app Uv" + assert app_uv, "Cannot find app uv" # Add " around exe if needed for Windows exes = {"uv": app_uv["executable"]} diff --git a/examples/python_uv/project_setup.py b/examples/python_uv/project_setup.py index 94848473e..59d0960e9 100644 --- a/examples/python_uv/project_setup.py +++ b/examples/python_uv/project_setup.py @@ -90,7 +90,7 @@ def create_project(client, num_jobs): log.debug("=== Define Task") task_def = TaskDefinition( name="plotting", - software_requirements=[Software(name="Uv")], + software_requirements=[Software(name="uv")], resource_requirements=ResourceRequirements( num_cores=0.5, memory=100 * 1024 * 1024, # 100 MB From 19f2a72ac2014a52d905eef4e10f4f4b5e02a4a9 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Mon, 30 Jun 2025 11:54:02 +0200 Subject: [PATCH 10/46] Link uv on first mention --- examples/python_uv/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/python_uv/README.md b/examples/python_uv/README.md index d05f56765..99d4ff976 100644 --- a/examples/python_uv/README.md +++ b/examples/python_uv/README.md @@ -1,5 +1,5 @@ # Running arbitrary python scripts on HPS -This example shows how arbitrary python scripts can be run on HPS, by using uv to generate +This example shows how arbitrary python scripts can be run on HPS, by using [uv](https://docs.astral.sh/uv/) to generate the required environments on the fly. The example sets up a project that will plot `sin(x)` using numpy and matplotlib, and then save From e9a367c1b4c5f83af57c8d4f5a6f292e0f33d5b6 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Mon, 30 Jun 2025 12:09:06 +0200 Subject: [PATCH 11/46] cleanup --- examples/python_uv/exec_script.py | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/python_uv/exec_script.py b/examples/python_uv/exec_script.py index ae4fbed60..cea84514a 100644 --- a/examples/python_uv/exec_script.py +++ b/examples/python_uv/exec_script.py @@ -44,7 +44,6 @@ def execute(self): # Identify applications app_uv = next((a for a in self.context.software if a["name"] == "uv"), None) - # app_xvfb = next((a for a in self.context.software if a["name"] == "Xvfb-run"), None) assert app_uv, "Cannot find app uv" # Add " around exe if needed for Windows From d91dcd82bcb7a2269dc480707ef09b4c369ae3c4 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Mon, 30 Jun 2025 12:09:21 +0200 Subject: [PATCH 12/46] fix typo --- examples/python_uv/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/python_uv/README.md b/examples/python_uv/README.md index 99d4ff976..d24cfb82e 100644 --- a/examples/python_uv/README.md +++ b/examples/python_uv/README.md @@ -51,7 +51,7 @@ Note that the version should be adjusted to the case at hand. The above steps setup uv with the cache located in a shared folder, such that any dependency will only need to be downloaded once. However, if the bandwidth between this shared folder and the -compute nodes is relatively slow, this can lead to long venv setuo times (on pclusters, a dozen +compute nodes is relatively slow, this can lead to long venv setup times (on pclusters, a dozen seconds is typical for reasonably large dependencies). Instead, uv can also be setup to use node local caching: From 41c7c012951ba0cb6196f5c1c0c4490dfe9f1370 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Mon, 30 Jun 2025 12:25:13 +0200 Subject: [PATCH 13/46] By default, don't clean up --- examples/python_uv/eval.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/python_uv/eval.py b/examples/python_uv/eval.py index 9ba5ca749..4434b806c 100644 --- a/examples/python_uv/eval.py +++ b/examples/python_uv/eval.py @@ -28,8 +28,6 @@ # ] # /// -import json -import sys import matplotlib.pyplot as plt import numpy as np @@ -46,5 +44,5 @@ plt.savefig("plot.png", dpi=200) # Communicate location of venv for cleanup - with open("output_parameters.json", "w") as out_file: - json.dump({"exe": sys.executable}, out_file, indent=4) + # with open("output_parameters.json", "w") as out_file: + # json.dump({"exe": sys.executable}, out_file, indent=4) From acaaf560802f512509fc1587c286c3d8469a5362 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Mon, 30 Jun 2025 12:43:44 +0200 Subject: [PATCH 14/46] No venv cleanup --- examples/python_uv/eval.py | 4 ---- examples/python_uv/exec_script.py | 16 +--------------- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/examples/python_uv/eval.py b/examples/python_uv/eval.py index 4434b806c..2c58ec84c 100644 --- a/examples/python_uv/eval.py +++ b/examples/python_uv/eval.py @@ -42,7 +42,3 @@ ax.set_xlabel("Time [s]") ax.set_ylabel("Displacement [cm]") plt.savefig("plot.png", dpi=200) - - # Communicate location of venv for cleanup - # with open("output_parameters.json", "w") as out_file: - # json.dump({"exe": sys.executable}, out_file, indent=4) diff --git a/examples/python_uv/exec_script.py b/examples/python_uv/exec_script.py index cea84514a..63620ebcc 100644 --- a/examples/python_uv/exec_script.py +++ b/examples/python_uv/exec_script.py @@ -27,7 +27,6 @@ import json import os -import shutil from ansys.rep.common.logging import log from ansys.rep.evaluator.task_manager import ApplicationExecution @@ -61,6 +60,7 @@ def execute(self): self.run_and_capture_output(cmd, shell=True, env=env) # Extract parameters if needed + output_parameters = {} try: log.debug(f"Loading output parameters from {output_filename}") with open(output_filename) as out_file: @@ -71,18 +71,4 @@ def execute(self): log.info("No output parameters found.") log.debug(f"Failed to read output_parameters from file: {ex}") - # Clean up venv cache - if "exe" in output_parameters.keys(): - try: - venv_cache = os.path.abspath(os.path.join(output_parameters["exe"], "..", "..")) - venv_cache_parent = os.path.join(venv_cache, "..") - if os.path.exists(venv_cache): - log.debug(f"Current venv cache: {os.listdir(venv_cache_parent)}") - log.debug(f"Cleaning venv cache at {venv_cache}...") - shutil.rmtree(venv_cache) - else: - log.debug(f"Venv cache path {venv_cache} does not exist.") - except Exception as ex: - log.debug(f"Couldn't clean venv cache at {venv_cache}: {ex}") - log.info("End Python execution script") From 627dfdad7747c7a65d09c8c60c6ed8b304a18771 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Mon, 30 Jun 2025 12:46:18 +0200 Subject: [PATCH 15/46] Remove mentions of pcluster, refactor cache explanation --- examples/python_uv/README.md | 47 ++++++++++++------------------------ 1 file changed, 15 insertions(+), 32 deletions(-) diff --git a/examples/python_uv/README.md b/examples/python_uv/README.md index d24cfb82e..b02672b68 100644 --- a/examples/python_uv/README.md +++ b/examples/python_uv/README.md @@ -22,51 +22,34 @@ More information can be found [here (python.org)](https://packaging.python.org/e # Prerequisites In order for the example to run, `uv` must be installed and registered on the scaler/evaluator. -On a pcluster, this can be done in the following way: +Instructions to this end can be found [here](https://docs.astral.sh/uv/getting-started/installation/). -```bash -pip3 install --target=/ansys_inc/uv uv # Install uv via pip -mkdir -p /shared/rep_file_storage/uv/uv_cache # Create cache dir -``` - -Note that the directories `/ansys_inc/uv` and `/shared/rep_file_storage/uv/uv_cache` are shared -directories, they are both accessible by all evaluators. - -Next, the application must be registered in the scaler/evaluator with the following properties: +Once uv is installed, the application must be registered in the scaler/evaluator with the following properties: | **Property** | **Value** | |-------------------|---------------------------| -| Name | uv | +| Name | uv | | Version | 0.6.14 | -| Installation Path | /ansys_inc/uv | -| Executable | /ansys_inc/uv/bin/uv | - -and the following environment variable: - -| **Env Variable** | **Value** | -|------------------|--------------------------------------| -| UV_CACHE_DIR | /shared/rep_file_storage/uv/uv_cache | +| Installation Path | /path/to/uv | +| Executable | /path/to/uv/bin/uv | Note that the version should be adjusted to the case at hand. -The above steps setup uv with the cache located in a shared folder, such that any dependency will -only need to be downloaded once. However, if the bandwidth between this shared folder and the -compute nodes is relatively slow, this can lead to long venv setup times (on pclusters, a dozen -seconds is typical for reasonably large dependencies). +By default, the uv cache is located in the home directory (~/.cache/uv). -Instead, uv can also be setup to use node local caching: +## Custom cache directory +The above steps setup uv with the cache located in its default location in the user home directory (~/.cache/uv). +Depending on the case at hand, other cache locations may be preferred, such as a globally accessible +cache used by all evaluators. An important observation is that venv setup time (in the cached case) +is largely dictated by the transfer bandwidth between the cache directory and the computing machine. +Setup times on the order of 100 ms are achievable for reasonably large dependencies. -## Node local cache -In cases where many short tasks are run, shorter runtimes can often be found by relocating the -uv cache to node local storage. In such a setup, each compute node has its own cache, meaning each -node will have to download all dependencies individually. Once the required packages are cached, -venv setup times will be on the order of 100 ms, 2 orders of magnitude faster than for the shared -case. To use node-local cache, the cache dir must be set to a directory local to each node. On -pclusters, for example, one could use the following value: +In order to define a custom uv cache directory, the following environment variable can be added to +the uv application registration in the scaler/evaluator: | **Env Variable** | **Value** | |------------------|--------------------------------------| -| UV_CACHE_DIR | /tmp/scratch/uv_cache | +| UV_CACHE_DIR | /path/to/custom/uv/cache/dir | ## Airgapped setups For airgapped setups where no internet connectivity is available, there are several options for a From 95981961d93b685bc86653b75b652d318dd2a783 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Mon, 30 Jun 2025 14:50:10 +0200 Subject: [PATCH 16/46] Make more consistent --- examples/python_uv/README.md | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/examples/python_uv/README.md b/examples/python_uv/README.md index b02672b68..982740263 100644 --- a/examples/python_uv/README.md +++ b/examples/python_uv/README.md @@ -3,7 +3,7 @@ This example shows how arbitrary python scripts can be run on HPS, by using [uv] the required environments on the fly. The example sets up a project that will plot `sin(x)` using numpy and matplotlib, and then save -the figure to a file. After the run is completed, the generated ephemeral venv is cleaned up. +the figure to a file. The main feature enabling uv to take care of the environment setup is the metadata header present in the `eval.py` script, which defines the dependencies: @@ -22,7 +22,7 @@ More information can be found [here (python.org)](https://packaging.python.org/e # Prerequisites In order for the example to run, `uv` must be installed and registered on the scaler/evaluator. -Instructions to this end can be found [here](https://docs.astral.sh/uv/getting-started/installation/). +Installation instructions can be found [here](https://docs.astral.sh/uv/getting-started/installation/). Once uv is installed, the application must be registered in the scaler/evaluator with the following properties: @@ -35,16 +35,10 @@ Once uv is installed, the application must be registered in the scaler/evaluator Note that the version should be adjusted to the case at hand. -By default, the uv cache is located in the home directory (~/.cache/uv). - ## Custom cache directory The above steps setup uv with the cache located in its default location in the user home directory (~/.cache/uv). -Depending on the case at hand, other cache locations may be preferred, such as a globally accessible -cache used by all evaluators. An important observation is that venv setup time (in the cached case) -is largely dictated by the transfer bandwidth between the cache directory and the computing machine. -Setup times on the order of 100 ms are achievable for reasonably large dependencies. - -In order to define a custom uv cache directory, the following environment variable can be added to +Depending on the individual situation, other cache locations may be preferred, such as a shared directory +accessible to all evaluators. In order to define a custom uv cache directory, the following environment variable can be added to the uv application registration in the scaler/evaluator: | **Env Variable** | **Value** | From 92f914f6e365d798fe3b7a990238fd2f79d63592 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Mon, 30 Jun 2025 15:28:23 +0200 Subject: [PATCH 17/46] Fixed command --- examples/python_uv/exec_script.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/python_uv/exec_script.py b/examples/python_uv/exec_script.py index 63620ebcc..4de062657 100644 --- a/examples/python_uv/exec_script.py +++ b/examples/python_uv/exec_script.py @@ -20,9 +20,9 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -"""Simplistic execution script for Python with uv. +"""Simple execution script for Python with uv. -Command formed: uv run +Command formed: uv run """ import json From c4668b89f6f5f3eaedfcb74cf26568462379bd6e Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Mon, 30 Jun 2025 16:08:19 +0200 Subject: [PATCH 18/46] Title --- examples/python_uv/project_setup.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/python_uv/project_setup.py b/examples/python_uv/project_setup.py index 59d0960e9..960da5bed 100644 --- a/examples/python_uv/project_setup.py +++ b/examples/python_uv/project_setup.py @@ -27,6 +27,8 @@ # ] # /// +"""Python with uv example.""" + import argparse import logging import os From b0bdb225c09e4eb4882e8d2ec48f1be1e692f4c8 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Mon, 30 Jun 2025 16:08:49 +0200 Subject: [PATCH 19/46] Move to .rst --- examples/python_uv/README.md | 84 ------------------------------------ 1 file changed, 84 deletions(-) delete mode 100644 examples/python_uv/README.md diff --git a/examples/python_uv/README.md b/examples/python_uv/README.md deleted file mode 100644 index 982740263..000000000 --- a/examples/python_uv/README.md +++ /dev/null @@ -1,84 +0,0 @@ -# Running arbitrary python scripts on HPS -This example shows how arbitrary python scripts can be run on HPS, by using [uv](https://docs.astral.sh/uv/) to generate -the required environments on the fly. - -The example sets up a project that will plot `sin(x)` using numpy and matplotlib, and then save -the figure to a file. - -The main feature enabling uv to take care of the environment setup is the metadata header present -in the `eval.py` script, which defines the dependencies: - -```python -# /// script -# requires-python = "==3.12" -# dependencies = [ -# "numpy", -# "matplotlib" -# ] -# /// -``` - -More information can be found [here (python.org)](https://packaging.python.org/en/latest/specifications/inline-script-metadata/#inline-script-metadata) and [here (astral.sh)](https://docs.astral.sh/uv/guides/scripts/#running-a-script-with-dependencies). - -# Prerequisites -In order for the example to run, `uv` must be installed and registered on the scaler/evaluator. -Installation instructions can be found [here](https://docs.astral.sh/uv/getting-started/installation/). - -Once uv is installed, the application must be registered in the scaler/evaluator with the following properties: - -| **Property** | **Value** | -|-------------------|---------------------------| -| Name | uv | -| Version | 0.6.14 | -| Installation Path | /path/to/uv | -| Executable | /path/to/uv/bin/uv | - -Note that the version should be adjusted to the case at hand. - -## Custom cache directory -The above steps setup uv with the cache located in its default location in the user home directory (~/.cache/uv). -Depending on the individual situation, other cache locations may be preferred, such as a shared directory -accessible to all evaluators. In order to define a custom uv cache directory, the following environment variable can be added to -the uv application registration in the scaler/evaluator: - -| **Env Variable** | **Value** | -|------------------|--------------------------------------| -| UV_CACHE_DIR | /path/to/custom/uv/cache/dir | - -## Airgapped setups -For airgapped setups where no internet connectivity is available, there are several options for a -successful uv setup: - -1. Pre-populate the uv cache with all desired dependencies. -2. Provide a local python package index, and set uv to use it. More information can be found -[here](https://docs.astral.sh/uv/configuration/indexes/). This index could then sit in a shared -location, with node-local caching applied. -3. Use pre-generated virtual environments, see [here](https://docs.astral.sh/uv/reference/cli/#uv-venv) - -In order to disable network access, one can either set the `UV_OFFLINE` environment variable, or -use the `--offline` flag with many uv commands. - -# Running the example -To run the example, execute the `project_setup.py` script, for example via `uv run project_setup.py`. -This will setup a project with a number of jobs, and each job will generate a `plot.png` file. - - -## Options -The example supports the following command line arguments: - -| **Flag** | **Example** | **Description** | -|------------------------|----------------------------------|---------------------------------------------------------| -| -U, --url | --url=https://localhost:8443/hps |URL of the target HPS instance | -| -u, --username | --username=repuser |Username to log into HPS | -| -p, --password | --password=topSecret |Password to log into HPS | -| -j, --num-jobs | --num-jobs=10 |Number of jobs to generate | - -## Files -The relevant files of the example are: - -- `project_setup.py`: Handles all communication with the HPS instance. Defines the project and -generates the jobs. -- `eval.py`: The script that is evaluated on HPS. Contains the code to plot a sine, and then save -the figure. -- `exec_script.py`: Execution script that uses uv to run the evaluation script. Also cleans up the -venv generated by uv. \ No newline at end of file From 8e3dd547ddc618e474afdc9da0091749dfa72d89 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Mon, 30 Jun 2025 16:09:33 +0200 Subject: [PATCH 20/46] Move to .rst --- doc/source/examples/ex_python_uv.rst | 146 +++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 doc/source/examples/ex_python_uv.rst diff --git a/doc/source/examples/ex_python_uv.rst b/doc/source/examples/ex_python_uv.rst new file mode 100644 index 000000000..0ce60b2f9 --- /dev/null +++ b/doc/source/examples/ex_python_uv.rst @@ -0,0 +1,146 @@ +.. _example_python_uv: + +.. note:: + + Go to the `bottom of this page`_ to download the ZIP file for the two-bar truss example. + +Running arbitrary python scripts on HPS +======================================= + +This example shows how arbitrary python scripts can be run on HPS, by +using `uv `__ to generate the required +environments on the fly. + +The example sets up a project that will plot ``sin(x)`` using numpy and +matplotlib, and then save the figure to a file. + +The main feature enabling uv to take care of the environment setup is +the metadata header present in the ``eval.py`` script, which defines the +dependencies: + +.. code:: python + + # /// script + # requires-python = "==3.12" + # dependencies = [ + # "numpy", + # "matplotlib" + # ] + # /// + +More information can be found `here +(python.org) `__ +and `here +(astral.sh) `__. + +Prerequisites +============= + +In order for the example to run, ``uv`` must be installed and registered +on the scaler/evaluator. Installation instructions can be found +`here `__. + +Once uv is installed, the application must be registered in the +scaler/evaluator with the following properties: + +================= ================== +**Property** **Value** +================= ================== +Name uv +Version 0.6.14 +Installation Path /path/to/uv +Executable /path/to/uv/bin/uv +================= ================== + +Note that the version should be adjusted to the case at hand. + +Custom cache directory +---------------------- + +The above steps setup uv with the cache located in its default location +in the user home directory (~/.cache/uv). Depending on the individual +situation, other cache locations may be preferred, such as a shared +directory accessible to all evaluators. In order to define a custom uv +cache directory, the following environment variable can be added to the +uv application registration in the scaler/evaluator: + +================ ============================ +**Env Variable** **Value** +================ ============================ +UV_CACHE_DIR /path/to/custom/uv/cache/dir +================ ============================ + +Airgapped setups +---------------- + +For airgapped setups where no internet connectivity is available, there +are several options for a successful uv setup: + +1. Pre-populate the uv cache with all desired dependencies. +2. Provide a local python package index, and set uv to use it. More + information can be found + `here `__. This + index could then sit in a shared location, with node-local caching + applied. +3. Use pre-generated virtual environments, see + `here `__ + +In order to disable network access, one can either set the +``UV_OFFLINE`` environment variable, or use the ``--offline`` flag with +many uv commands. + +Running the example +=================== + +To run the example, execute the ``project_setup.py`` script, for example +via ``uv run project_setup.py``. This will setup a project with a number +of jobs, and each job will generate a ``plot.png`` file. + +Options +------- + +The example supports the following command line arguments: + ++--------------+---------------------------------+----------------------------------+ +| **Flag** | **Example** | **Description** | ++==============+=================================+==================================+ +| -U, –url | –url=https://localhost:8443/hps | URL of the target HPS instance | ++--------------+---------------------------------+----------------------------------+ +| -u, | –username=repuser | Username to log into HPS | +| –username | | | ++--------------+---------------------------------+----------------------------------+ +| -p, | –password=topSecret | Password to log into HPS | +| –password | | | ++--------------+---------------------------------+----------------------------------+ +| -j, | –num-jobs=10 | Number of jobs to generate | +| –num-jobs | | | ++--------------+---------------------------------+----------------------------------+ + +Files +----- + +The relevant files of the example are: + +- ``project_setup.py``: Handles all communication with the HPS instance. + Defines the project and generates the jobs. + +.. literalinclude:: ../../../examples/python_uv/project_setup.py + :language: python + :lines: 23- + :caption: project_setup.py + +- ``eval.py``: The script that is evaluated on HPS. Contains the code to + plot a sine, and then save the figure. + +.. literalinclude:: ../../../examples/python_uv/eval.py + :language: python + :lines: 23- + :caption: eval.py + +- ``exec_script.py``: Execution script that uses uv to run the + evaluation script. + +.. literalinclude:: ../../../examples/python_uv/exec_script.py + :language: python + :lines: 23- + :caption: exec_script.py \ No newline at end of file From 433c5d0afca0495c886b4703c6201496062f3370 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Mon, 30 Jun 2025 16:10:10 +0200 Subject: [PATCH 21/46] Added new example doc --- doc/source/examples/index.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/source/examples/index.rst b/doc/source/examples/index.rst index 3e6f5f54f..b2bc0f91a 100644 --- a/doc/source/examples/index.rst +++ b/doc/source/examples/index.rst @@ -35,6 +35,7 @@ one :download:`ZIP file <../../../build/pyhps_examples.zip>`. ex_fluent_nozzle ex_cfx_static_mixer ex_python_two_bar + ex_python_uv .. list-table:: :header-rows: 1 @@ -59,6 +60,8 @@ one :download:`ZIP file <../../../build/pyhps_examples.zip>`. - Submit a CFX solve job to the HPS server using an execution script. * - :ref:`example_python_two_bar` - Create an HPS project that solves a two-bar truss problem with Python. + * - :ref:`example_python_uv` + - Run arbitrary Python scripts on HPS using uv. A link to download the required resources is available on each example page. If desired, you can download the required resources for all examples through the link below. From 0d02bda2996078adb89136ebd5fde0fcbb137c70 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Mon, 30 Jun 2025 16:13:54 +0200 Subject: [PATCH 22/46] remove bullets --- doc/source/examples/ex_python_uv.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/source/examples/ex_python_uv.rst b/doc/source/examples/ex_python_uv.rst index 0ce60b2f9..9dde85f85 100644 --- a/doc/source/examples/ex_python_uv.rst +++ b/doc/source/examples/ex_python_uv.rst @@ -121,7 +121,7 @@ Files The relevant files of the example are: -- ``project_setup.py``: Handles all communication with the HPS instance. +``project_setup.py``: Handles all communication with the HPS instance. Defines the project and generates the jobs. .. literalinclude:: ../../../examples/python_uv/project_setup.py @@ -129,7 +129,7 @@ The relevant files of the example are: :lines: 23- :caption: project_setup.py -- ``eval.py``: The script that is evaluated on HPS. Contains the code to +``eval.py``: The script that is evaluated on HPS. Contains the code to plot a sine, and then save the figure. .. literalinclude:: ../../../examples/python_uv/eval.py @@ -137,7 +137,7 @@ The relevant files of the example are: :lines: 23- :caption: eval.py -- ``exec_script.py``: Execution script that uses uv to run the +``exec_script.py``: Execution script that uses uv to run the evaluation script. .. literalinclude:: ../../../examples/python_uv/exec_script.py From 2be533c8d6f5a42b2b0e11cf6cf6ef6b57c4bb4a Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Mon, 7 Jul 2025 10:08:53 +0200 Subject: [PATCH 23/46] Fix some vale complaints --- doc/source/examples/ex_python_uv.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/source/examples/ex_python_uv.rst b/doc/source/examples/ex_python_uv.rst index 9dde85f85..5d29bdb92 100644 --- a/doc/source/examples/ex_python_uv.rst +++ b/doc/source/examples/ex_python_uv.rst @@ -11,7 +11,7 @@ This example shows how arbitrary python scripts can be run on HPS, by using `uv `__ to generate the required environments on the fly. -The example sets up a project that will plot ``sin(x)`` using numpy and +The example sets up a project that shall plot ``sin(x)`` using numpy and matplotlib, and then save the figure to a file. The main feature enabling uv to take care of the environment setup is @@ -40,7 +40,7 @@ In order for the example to run, ``uv`` must be installed and registered on the scaler/evaluator. Installation instructions can be found `here `__. -Once uv is installed, the application must be registered in the +Once uv is installed, the app must be registered in the scaler/evaluator with the following properties: ================= ================== @@ -57,12 +57,12 @@ Note that the version should be adjusted to the case at hand. Custom cache directory ---------------------- -The above steps setup uv with the cache located in its default location +The preceding steps setup uv with the cache located in its default location in the user home directory (~/.cache/uv). Depending on the individual situation, other cache locations may be preferred, such as a shared directory accessible to all evaluators. In order to define a custom uv cache directory, the following environment variable can be added to the -uv application registration in the scaler/evaluator: +uv app registration in the scaler/evaluator: ================ ============================ **Env Variable** **Value** @@ -85,7 +85,7 @@ are several options for a successful uv setup: 3. Use pre-generated virtual environments, see `here `__ -In order to disable network access, one can either set the +In order to turn off network access, one can either set the ``UV_OFFLINE`` environment variable, or use the ``--offline`` flag with many uv commands. @@ -93,8 +93,8 @@ Running the example =================== To run the example, execute the ``project_setup.py`` script, for example -via ``uv run project_setup.py``. This will setup a project with a number -of jobs, and each job will generate a ``plot.png`` file. +via ``uv run project_setup.py``. This sets up a project with a number +of jobs, and each job shall generate a ``plot.png`` file. Options ------- From c5fa60f2b323c0c2b4bdc7d525bd4e003dc12352 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Mon, 7 Jul 2025 10:22:19 +0200 Subject: [PATCH 24/46] Added matplotlib, numpy, uv to the vocabulary --- doc/styles/config/vocabularies/ANSYS/accept.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/styles/config/vocabularies/ANSYS/accept.txt b/doc/styles/config/vocabularies/ANSYS/accept.txt index 9f40ae5f3..7518b536b 100644 --- a/doc/styles/config/vocabularies/ANSYS/accept.txt +++ b/doc/styles/config/vocabularies/ANSYS/accept.txt @@ -9,10 +9,13 @@ isort [Kk]eycloak Koenig marshmallow_oneofschema +matplotlib +numpy Mises postprocess [Pp]ydantic pytest subpackage +uv von Wintermantel \ No newline at end of file From e221cbb6354a3c57bebccb230d71cbb80ccb72bf Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Mon, 7 Jul 2025 10:22:43 +0200 Subject: [PATCH 25/46] More vale consistency --- doc/source/examples/ex_python_uv.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/source/examples/ex_python_uv.rst b/doc/source/examples/ex_python_uv.rst index 5d29bdb92..26b87d1eb 100644 --- a/doc/source/examples/ex_python_uv.rst +++ b/doc/source/examples/ex_python_uv.rst @@ -65,15 +65,15 @@ cache directory, the following environment variable can be added to the uv app registration in the scaler/evaluator: ================ ============================ -**Env Variable** **Value** +**Environment Variable** **Value** ================ ============================ UV_CACHE_DIR /path/to/custom/uv/cache/dir ================ ============================ -Airgapped setups +Air gapped setups ---------------- -For airgapped setups where no internet connectivity is available, there +For air gapped setups where no internet connectivity is available, there are several options for a successful uv setup: 1. Pre-populate the uv cache with all desired dependencies. From 1ac9deb1059b58a1b3b63b6c9184124f2f8cb98a Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Mon, 7 Jul 2025 10:25:48 +0200 Subject: [PATCH 26/46] Maybe it likes air-gapped? --- doc/source/examples/ex_python_uv.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/examples/ex_python_uv.rst b/doc/source/examples/ex_python_uv.rst index 26b87d1eb..5c0e2eea8 100644 --- a/doc/source/examples/ex_python_uv.rst +++ b/doc/source/examples/ex_python_uv.rst @@ -70,10 +70,10 @@ uv app registration in the scaler/evaluator: UV_CACHE_DIR /path/to/custom/uv/cache/dir ================ ============================ -Air gapped setups +Air-gapped setups ---------------- -For air gapped setups where no internet connectivity is available, there +For air-gapped setups where no internet connectivity is available, there are several options for a successful uv setup: 1. Pre-populate the uv cache with all desired dependencies. From 35fee734b35e985585c8bf03813c6ba94f6c8bb7 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Mon, 7 Jul 2025 10:31:46 +0200 Subject: [PATCH 27/46] Application is just so much better than app... --- doc/source/examples/ex_python_uv.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/examples/ex_python_uv.rst b/doc/source/examples/ex_python_uv.rst index 5c0e2eea8..f65e475c4 100644 --- a/doc/source/examples/ex_python_uv.rst +++ b/doc/source/examples/ex_python_uv.rst @@ -40,7 +40,7 @@ In order for the example to run, ``uv`` must be installed and registered on the scaler/evaluator. Installation instructions can be found `here `__. -Once uv is installed, the app must be registered in the +Once uv is installed, the application must be registered in the scaler/evaluator with the following properties: ================= ================== @@ -62,7 +62,7 @@ in the user home directory (~/.cache/uv). Depending on the individual situation, other cache locations may be preferred, such as a shared directory accessible to all evaluators. In order to define a custom uv cache directory, the following environment variable can be added to the -uv app registration in the scaler/evaluator: +uv application registration in the scaler/evaluator: ================ ============================ **Environment Variable** **Value** From 12c7d2fc8d1349dae40af0e9254923bb05f75187 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Mon, 7 Jul 2025 11:32:55 +0200 Subject: [PATCH 28/46] Add footer, fix header --- doc/source/examples/ex_python_uv.rst | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/doc/source/examples/ex_python_uv.rst b/doc/source/examples/ex_python_uv.rst index f65e475c4..c8581b579 100644 --- a/doc/source/examples/ex_python_uv.rst +++ b/doc/source/examples/ex_python_uv.rst @@ -2,7 +2,7 @@ .. note:: - Go to the `bottom of this page`_ to download the ZIP file for the two-bar truss example. + Go to the `bottom of this page`_ to download the ZIP file for the uv example. Running arbitrary python scripts on HPS ======================================= @@ -143,4 +143,15 @@ The relevant files of the example are: .. literalinclude:: ../../../examples/python_uv/exec_script.py :language: python :lines: 23- - :caption: exec_script.py \ No newline at end of file + :caption: exec_script.py + +Download the ZIP file for the uv example and use +a tool such as 7-Zip to extract the files. + +.. _bottom of this page: + +.. button-link:: ../_downloads/python_uv.zip + :color: black + :expand: + + Download ZIP file \ No newline at end of file From 2f9433f871c9986501674ea2d1792087e4795cc5 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Mon, 7 Jul 2025 11:42:38 +0200 Subject: [PATCH 29/46] Fix dash conversion issue --- doc/source/examples/ex_python_uv.rst | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/doc/source/examples/ex_python_uv.rst b/doc/source/examples/ex_python_uv.rst index c8581b579..9a081d517 100644 --- a/doc/source/examples/ex_python_uv.rst +++ b/doc/source/examples/ex_python_uv.rst @@ -101,20 +101,20 @@ Options The example supports the following command line arguments: -+--------------+---------------------------------+----------------------------------+ -| **Flag** | **Example** | **Description** | -+==============+=================================+==================================+ -| -U, –url | –url=https://localhost:8443/hps | URL of the target HPS instance | -+--------------+---------------------------------+----------------------------------+ -| -u, | –username=repuser | Username to log into HPS | -| –username | | | -+--------------+---------------------------------+----------------------------------+ -| -p, | –password=topSecret | Password to log into HPS | -| –password | | | -+--------------+---------------------------------+----------------------------------+ -| -j, | –num-jobs=10 | Number of jobs to generate | -| –num-jobs | | | -+--------------+---------------------------------+----------------------------------+ ++--------------+----------------------------------+----------------------------------+ +| **Flag** | **Example** | **Description** | ++==============+==================================+==================================+ +| -U, --url | --url=https://localhost:8443/hps | URL of the target HPS instance | ++--------------+----------------------------------+----------------------------------+ +| -u, | --username=repuser | Username to log into HPS | +| --username | | | ++--------------+----------------------------------+----------------------------------+ +| -p, | --password=topSecret | Password to log into HPS | +| --password | | | ++--------------+----------------------------------+----------------------------------+ +| -j, | --num-jobs=10 | Number of jobs to generate | +| --num-jobs | | | ++--------------+----------------------------------+----------------------------------+ Files ----- From 306d687cd5f72e187fe7955db60a7064e4b8cdcd Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Mon, 7 Jul 2025 11:54:44 +0200 Subject: [PATCH 30/46] Fix tables --- doc/source/examples/ex_python_uv.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/source/examples/ex_python_uv.rst b/doc/source/examples/ex_python_uv.rst index 9a081d517..68818c6c5 100644 --- a/doc/source/examples/ex_python_uv.rst +++ b/doc/source/examples/ex_python_uv.rst @@ -64,11 +64,11 @@ directory accessible to all evaluators. In order to define a custom uv cache directory, the following environment variable can be added to the uv application registration in the scaler/evaluator: -================ ============================ +======================== ============================ **Environment Variable** **Value** -================ ============================ -UV_CACHE_DIR /path/to/custom/uv/cache/dir -================ ============================ +======================== ============================ +UV_CACHE_DIR /path/to/custom/uv/cache/dir +======================== ============================ Air-gapped setups ---------------- From eb58dbf989ea6d9c425914fe556de27a1bf6c8f5 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Mon, 7 Jul 2025 12:28:48 +0200 Subject: [PATCH 31/46] python->Python --- doc/source/examples/ex_python_uv.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/source/examples/ex_python_uv.rst b/doc/source/examples/ex_python_uv.rst index 68818c6c5..0e8074e62 100644 --- a/doc/source/examples/ex_python_uv.rst +++ b/doc/source/examples/ex_python_uv.rst @@ -4,10 +4,10 @@ Go to the `bottom of this page`_ to download the ZIP file for the uv example. -Running arbitrary python scripts on HPS +Running arbitrary Python scripts on HPS ======================================= -This example shows how arbitrary python scripts can be run on HPS, by +This example shows how arbitrary Python scripts can be run on HPS, by using `uv `__ to generate the required environments on the fly. @@ -77,7 +77,7 @@ For air-gapped setups where no internet connectivity is available, there are several options for a successful uv setup: 1. Pre-populate the uv cache with all desired dependencies. -2. Provide a local python package index, and set uv to use it. More +2. Provide a local Python package index, and set uv to use it. More information can be found `here `__. This index could then sit in a shared location, with node-local caching From 0f92354c55c51dd97d253ab2774f6fd1f8fe6ea8 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Tue, 8 Jul 2025 12:28:41 +0200 Subject: [PATCH 32/46] Better table style --- doc/source/examples/ex_python_uv.rst | 65 ++++++++++++++++------------ 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/doc/source/examples/ex_python_uv.rst b/doc/source/examples/ex_python_uv.rst index 0e8074e62..e68f3058c 100644 --- a/doc/source/examples/ex_python_uv.rst +++ b/doc/source/examples/ex_python_uv.rst @@ -43,14 +43,19 @@ on the scaler/evaluator. Installation instructions can be found Once uv is installed, the application must be registered in the scaler/evaluator with the following properties: -================= ================== -**Property** **Value** -================= ================== -Name uv -Version 0.6.14 -Installation Path /path/to/uv -Executable /path/to/uv/bin/uv -================= ================== +.. list-table:: + :header-rows: 1 + + * - Property + - Value + * - Name + - uv + * - Version + - 0.7.19 + * - Installation Path + - /path/to/uv + * - Executable + - /path/to/uv/bin/uv Note that the version should be adjusted to the case at hand. @@ -64,11 +69,13 @@ directory accessible to all evaluators. In order to define a custom uv cache directory, the following environment variable can be added to the uv application registration in the scaler/evaluator: -======================== ============================ -**Environment Variable** **Value** -======================== ============================ -UV_CACHE_DIR /path/to/custom/uv/cache/dir -======================== ============================ +.. list-table:: + :header-rows: 1 + + * - Environment Variable + - Value + * - UV_CACHE_DIR + - /path/to/custom/uv/cache/dir Air-gapped setups ---------------- @@ -101,20 +108,24 @@ Options The example supports the following command line arguments: -+--------------+----------------------------------+----------------------------------+ -| **Flag** | **Example** | **Description** | -+==============+==================================+==================================+ -| -U, --url | --url=https://localhost:8443/hps | URL of the target HPS instance | -+--------------+----------------------------------+----------------------------------+ -| -u, | --username=repuser | Username to log into HPS | -| --username | | | -+--------------+----------------------------------+----------------------------------+ -| -p, | --password=topSecret | Password to log into HPS | -| --password | | | -+--------------+----------------------------------+----------------------------------+ -| -j, | --num-jobs=10 | Number of jobs to generate | -| --num-jobs | | | -+--------------+----------------------------------+----------------------------------+ +.. list-table:: + :header-rows: 1 + + * - Flag + - Example + - Description + * - -U, --url + - --url=https://localhost:8443/hps + - URL of the target HPS instance + * - -u, --username + - --username=repuser + - Username to log into HPS + * - -p, --password + - --password=topSecret + - Password to log into HPS + * - -j, --num-jobs + - --num-jobs=10 + - Number of jobs to generate Files ----- From dddb0a77c3519c4c15d874b36663e7c87d062702 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Wed, 30 Jul 2025 10:56:49 +0200 Subject: [PATCH 33/46] Add venv cleanup back with explanation of purpose --- examples/python_uv/exec_script.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/examples/python_uv/exec_script.py b/examples/python_uv/exec_script.py index 4de062657..77fcf7af0 100644 --- a/examples/python_uv/exec_script.py +++ b/examples/python_uv/exec_script.py @@ -27,6 +27,7 @@ import json import os +import shutil from ansys.rep.common.logging import log from ansys.rep.evaluator.task_manager import ApplicationExecution @@ -59,7 +60,7 @@ def execute(self): cmd = f"{exes['uv']} run {script_file['path']}" self.run_and_capture_output(cmd, shell=True, env=env) - # Extract parameters if needed + # Read eval.py output parameters output_parameters = {} try: log.debug(f"Loading output parameters from {output_filename}") @@ -71,4 +72,17 @@ def execute(self): log.info("No output parameters found.") log.debug(f"Failed to read output_parameters from file: {ex}") + # If exe path is in out params, clean up virtual environment to avoid runaway uv venv cache + # See https://github.com/astral-sh/uv/issues/13431 + if "exe" in output_parameters.keys(): + try: + venv_cache = os.path.abspath(os.path.join(output_parameters["exe"], "..", "..")) + if os.path.exists(venv_cache): + log.debug(f"Cleaning venv cache at {venv_cache}...") + shutil.rmtree(venv_cache) + else: + log.debug(f"Venv cache path {venv_cache} does not exist.") + except Exception as ex: + log.debug(f"Couldn't clean venv cache at {venv_cache}: {ex}") + log.info("End Python execution script") From 25a2f42d631f1a6f64d577370757dc2a80688961 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Wed, 30 Jul 2025 10:58:38 +0200 Subject: [PATCH 34/46] Add back exe path communication with explanation, but commented out by default --- examples/python_uv/eval.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/python_uv/eval.py b/examples/python_uv/eval.py index 2c58ec84c..06c106855 100644 --- a/examples/python_uv/eval.py +++ b/examples/python_uv/eval.py @@ -42,3 +42,7 @@ ax.set_xlabel("Time [s]") ax.set_ylabel("Displacement [cm]") plt.savefig("plot.png", dpi=200) + + # Uncomment to enable venv cleanup in exec script, see execution script for details + # with open("output_parameters.json", "w") as out_file: + # json.dump({"exe": sys.executable}, out_file, indent=4) From 5f49439ebe44a60ac73cfd910b7d316cf286dc5e Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Wed, 30 Jul 2025 11:07:28 +0200 Subject: [PATCH 35/46] Install pyhps from pypi --- examples/python_uv/project_setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/python_uv/project_setup.py b/examples/python_uv/project_setup.py index 960da5bed..803a675d5 100644 --- a/examples/python_uv/project_setup.py +++ b/examples/python_uv/project_setup.py @@ -23,7 +23,7 @@ # /// script # requires-python = "==3.10" # dependencies = [ -# "ansys-hps-client @ git+https://github.com/ansys/pyhps.git@main" +# "ansys-hps-client>=0.10" # ] # /// From a802c57f7ab107a86422c979da14584d98d7aeaf Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Wed, 30 Jul 2025 11:08:21 +0200 Subject: [PATCH 36/46] Register python_uv example --- doc/source/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/conf.py b/doc/source/conf.py index ffe438126..ef633da33 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -60,6 +60,7 @@ "StaticMixer_001.cfx", "StaticMixer_001.def", ], + "python_uv": ["project_setup.py", "exec_script.py", "eval.py"], } sys.path.append(os.path.abspath(os.path.dirname(__file__))) From c51c84bafe3f9c0f36d75dad0fb23480da653806 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Wed, 30 Jul 2025 11:24:38 +0200 Subject: [PATCH 37/46] Fix title underline length, cli args are code --- doc/source/examples/ex_python_uv.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/source/examples/ex_python_uv.rst b/doc/source/examples/ex_python_uv.rst index e68f3058c..feb71b76b 100644 --- a/doc/source/examples/ex_python_uv.rst +++ b/doc/source/examples/ex_python_uv.rst @@ -78,7 +78,7 @@ uv application registration in the scaler/evaluator: - /path/to/custom/uv/cache/dir Air-gapped setups ----------------- +----------------- For air-gapped setups where no internet connectivity is available, there are several options for a successful uv setup: @@ -114,17 +114,17 @@ The example supports the following command line arguments: * - Flag - Example - Description - * - -U, --url - - --url=https://localhost:8443/hps + * - ``-U``, ``--url`` + - ``--url=https://localhost:8443/hps`` - URL of the target HPS instance - * - -u, --username - - --username=repuser + * - ``-u``, ``--username`` + - ``--username=repuser`` - Username to log into HPS - * - -p, --password - - --password=topSecret + * - ``-p``, ``--password`` + - ``--password=topSecret`` - Password to log into HPS - * - -j, --num-jobs - - --num-jobs=10 + * - ``-j``, ``--num-jobs`` + - ``--num-jobs=10`` - Number of jobs to generate Files From 94cd742ab210752b367f0e1160efa035fdbb8682 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Wed, 30 Jul 2025 11:27:25 +0200 Subject: [PATCH 38/46] More dangerous comment --- examples/python_uv/exec_script.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/python_uv/exec_script.py b/examples/python_uv/exec_script.py index 77fcf7af0..bccecdfb1 100644 --- a/examples/python_uv/exec_script.py +++ b/examples/python_uv/exec_script.py @@ -72,7 +72,7 @@ def execute(self): log.info("No output parameters found.") log.debug(f"Failed to read output_parameters from file: {ex}") - # If exe path is in out params, clean up virtual environment to avoid runaway uv venv cache + # If exe path is in out params, delete the venv folder to avoid runaway uv venv cache # See https://github.com/astral-sh/uv/issues/13431 if "exe" in output_parameters.keys(): try: From 98c59730a4bd85067df4ac3d48d624540337bb22 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Wed, 30 Jul 2025 11:43:16 +0200 Subject: [PATCH 39/46] Fix formatting --- doc/source/examples/ex_python_uv.rst | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/doc/source/examples/ex_python_uv.rst b/doc/source/examples/ex_python_uv.rst index feb71b76b..3426491dd 100644 --- a/doc/source/examples/ex_python_uv.rst +++ b/doc/source/examples/ex_python_uv.rst @@ -132,24 +132,23 @@ Files The relevant files of the example are: -``project_setup.py``: Handles all communication with the HPS instance. - Defines the project and generates the jobs. +The project creation script ``project_setup.py``. Handles all communication with the HPS instance, +defines the project and generates the jobs. .. literalinclude:: ../../../examples/python_uv/project_setup.py :language: python :lines: 23- :caption: project_setup.py -``eval.py``: The script that is evaluated on HPS. Contains the code to - plot a sine, and then save the figure. +The script ``eval.py`` that is evaluated on HPS. Contains the code to plot a sine, and then save +the figure. .. literalinclude:: ../../../examples/python_uv/eval.py :language: python :lines: 23- :caption: eval.py -``exec_script.py``: Execution script that uses uv to run the - evaluation script. +The execution script ``exec_script.py`` that uses uv to run the evaluation script. .. literalinclude:: ../../../examples/python_uv/exec_script.py :language: python From 5ecbf513bb076c84ecec595172b1167e20a7d6ce Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Wed, 30 Jul 2025 14:15:05 +0200 Subject: [PATCH 40/46] No minimum version needed --- examples/python_uv/project_setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/python_uv/project_setup.py b/examples/python_uv/project_setup.py index 803a675d5..2e7ac1f86 100644 --- a/examples/python_uv/project_setup.py +++ b/examples/python_uv/project_setup.py @@ -23,7 +23,7 @@ # /// script # requires-python = "==3.10" # dependencies = [ -# "ansys-hps-client>=0.10" +# "ansys-hps-client" # ] # /// From 89a2de3a97f58dc176e2679418b52bba79ee254e Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Thu, 31 Jul 2025 08:58:25 +0200 Subject: [PATCH 41/46] Fix uv requirements --- examples/python_uv/project_setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/python_uv/project_setup.py b/examples/python_uv/project_setup.py index 2e7ac1f86..46afac654 100644 --- a/examples/python_uv/project_setup.py +++ b/examples/python_uv/project_setup.py @@ -23,7 +23,8 @@ # /// script # requires-python = "==3.10" # dependencies = [ -# "ansys-hps-client" +# "ansys-hps-client", +# "packaging" # ] # /// From fa72cadeb7be7276cdfd26fa831749c65c9b54a2 Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Thu, 31 Jul 2025 09:02:19 +0200 Subject: [PATCH 42/46] Added imports needed for venv cleanup --- examples/python_uv/eval.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/python_uv/eval.py b/examples/python_uv/eval.py index 06c106855..abaae2b80 100644 --- a/examples/python_uv/eval.py +++ b/examples/python_uv/eval.py @@ -44,5 +44,7 @@ plt.savefig("plot.png", dpi=200) # Uncomment to enable venv cleanup in exec script, see execution script for details + # import json + # import sys # with open("output_parameters.json", "w") as out_file: # json.dump({"exe": sys.executable}, out_file, indent=4) From d46f9db03d898d06bed1a32962ca5643700a6ce9 Mon Sep 17 00:00:00 2001 From: Pascal Engeler <32872038+engpas@users.noreply.github.com> Date: Mon, 11 Aug 2025 14:38:34 +0200 Subject: [PATCH 43/46] Apply suggestions from code review Co-authored-by: Kathy Pippert <84872299+PipKat@users.noreply.github.com> --- doc/source/examples/ex_python_uv.rst | 102 +++++++++--------- doc/source/examples/index.rst | 2 +- .../config/vocabularies/ANSYS/accept.txt | 4 +- 3 files changed, 56 insertions(+), 52 deletions(-) diff --git a/doc/source/examples/ex_python_uv.rst b/doc/source/examples/ex_python_uv.rst index 3426491dd..77401cbb7 100644 --- a/doc/source/examples/ex_python_uv.rst +++ b/doc/source/examples/ex_python_uv.rst @@ -4,19 +4,18 @@ Go to the `bottom of this page`_ to download the ZIP file for the uv example. -Running arbitrary Python scripts on HPS -======================================= +Run arbitrary Python scripts on HPS +=================================== -This example shows how arbitrary Python scripts can be run on HPS, by -using `uv `__ to generate the required +This example shows how to run arbitrary Python scripts. It uses +the `uv `__ package to generate the required environments on the fly. -The example sets up a project that shall plot ``sin(x)`` using numpy and -matplotlib, and then save the figure to a file. +The example sets up a project that plots ``sin(x)`` using NumPy and +Matplotlib and then saves the figure to a file. -The main feature enabling uv to take care of the environment setup is -the metadata header present in the ``eval.py`` script, which defines the -dependencies: +The metadata header present in the ``eval.py`` script, which defines the +dependencies, enables uv to take care of the environment setup: .. code:: python @@ -28,19 +27,20 @@ dependencies: # ] # /// -More information can be found `here -(python.org) `__ -and `here -(astral.sh) `__. +For more information, see `Inline script metadata +`__ +in the *Python Packaging User Guide* and `Running a script with dependencies `__ +in the uv documentation. Prerequisites ============= -In order for the example to run, ``uv`` must be installed and registered -on the scaler/evaluator. Installation instructions can be found -`here `__. +For the example to run, uv must be installed and registered +on the scaler/evaluator. For installation instructions, see +`Installing uv `__ +in the uv documentation. -Once uv is installed, the application must be registered in the +Once uv is installed, the package must be registered in the scaler/evaluator with the following properties: .. list-table:: @@ -59,15 +59,15 @@ scaler/evaluator with the following properties: Note that the version should be adjusted to the case at hand. -Custom cache directory ----------------------- +Define a custom cache directory +------------------------------- -The preceding steps setup uv with the cache located in its default location -in the user home directory (~/.cache/uv). Depending on the individual -situation, other cache locations may be preferred, such as a shared -directory accessible to all evaluators. In order to define a custom uv -cache directory, the following environment variable can be added to the -uv application registration in the scaler/evaluator: +The preceding steps set up uv with the cache located in its default location +in the user home directory (~/.cache/uv). Depending on your +situation, you might prefer a different cache location, such as a shared +directory accessible to all evaluators. To define a custom uv +cache directory, add the following environment variable to the +uv package registration in the scaler/evaluator: .. list-table:: :header-rows: 1 @@ -77,31 +77,35 @@ uv application registration in the scaler/evaluator: * - UV_CACHE_DIR - /path/to/custom/uv/cache/dir -Air-gapped setups ------------------ +Create offline air-gapped setups +-------------------------------- -For air-gapped setups where no internet connectivity is available, there -are several options for a successful uv setup: +If internet is not available, you can create offline air-gapped setups for uv +using one of these options: -1. Pre-populate the uv cache with all desired dependencies. -2. Provide a local Python package index, and set uv to use it. More - information can be found - `here `__. This - index could then sit in a shared location, with node-local caching - applied. -3. Use pre-generated virtual environments, see - `here `__ +* Pre-populate the uv cache with all desired dependencies. +* Provide a local Python package index and set uv to use it. For + more information, see + `Package indexes `__ + in the uv documentation. This index can then sit in a shared location, + with node-local caching applied. +* Use pre-generated virtual environments. For more information, see + `uv venv `__ in the + uv documentation. -In order to turn off network access, one can either set the -``UV_OFFLINE`` environment variable, or use the ``--offline`` flag with +To turn off network access, you can either set the +``UV_OFFLINE`` environment variable or use the ``--offline`` flag with many uv commands. -Running the example -=================== +Run the example +=============== -To run the example, execute the ``project_setup.py`` script, for example -via ``uv run project_setup.py``. This sets up a project with a number -of jobs, and each job shall generate a ``plot.png`` file. +To run the example, execute the ``project_setup.py`` script:: + +uv run project_setup.py + +This command sets up a project with a number +of jobs. Each job generates a ``plot.png`` file. Options ------- @@ -130,17 +134,17 @@ The example supports the following command line arguments: Files ----- -The relevant files of the example are: +Descriptions follow of the relevant example files. -The project creation script ``project_setup.py``. Handles all communication with the HPS instance, -defines the project and generates the jobs. +The project creation script, ``project_setup.py``, handles all communication with the +HPS instance, defines the project, and generates the jobs. .. literalinclude:: ../../../examples/python_uv/project_setup.py :language: python :lines: 23- :caption: project_setup.py -The script ``eval.py`` that is evaluated on HPS. Contains the code to plot a sine, and then save +The script ``eval.py``, which is evaluated on HPS, contains the code to plot a sine and then save the figure. .. literalinclude:: ../../../examples/python_uv/eval.py @@ -148,7 +152,7 @@ the figure. :lines: 23- :caption: eval.py -The execution script ``exec_script.py`` that uses uv to run the evaluation script. +The execution script, ``exec_script.py``, uses uv to run the evaluation script. .. literalinclude:: ../../../examples/python_uv/exec_script.py :language: python diff --git a/doc/source/examples/index.rst b/doc/source/examples/index.rst index b2bc0f91a..99d41f08f 100644 --- a/doc/source/examples/index.rst +++ b/doc/source/examples/index.rst @@ -64,7 +64,7 @@ one :download:`ZIP file <../../../build/pyhps_examples.zip>`. - Run arbitrary Python scripts on HPS using uv. A link to download the required resources is available on each example page. If desired, -you can download the required resources for all examples through the link below. +you can download the required resources for all examples using the following link. .. _bottom of this page: diff --git a/doc/styles/config/vocabularies/ANSYS/accept.txt b/doc/styles/config/vocabularies/ANSYS/accept.txt index 7518b536b..74f841bab 100644 --- a/doc/styles/config/vocabularies/ANSYS/accept.txt +++ b/doc/styles/config/vocabularies/ANSYS/accept.txt @@ -9,8 +9,8 @@ isort [Kk]eycloak Koenig marshmallow_oneofschema -matplotlib -numpy +Matplotlib +NumPy Mises postprocess [Pp]ydantic From 5bb244b0d3a4d7151d737b68599988af4260c64f Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Mon, 11 Aug 2025 16:26:58 +0200 Subject: [PATCH 44/46] Rewrite sentence as suggested --- doc/source/examples/ex_python_uv.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/examples/ex_python_uv.rst b/doc/source/examples/ex_python_uv.rst index 77401cbb7..7bca36955 100644 --- a/doc/source/examples/ex_python_uv.rst +++ b/doc/source/examples/ex_python_uv.rst @@ -57,7 +57,7 @@ scaler/evaluator with the following properties: * - Executable - /path/to/uv/bin/uv -Note that the version should be adjusted to the case at hand. +Be sure to adjust the version to the one you have installed. Define a custom cache directory ------------------------------- From fee1deb24d0de69c1cb4bd7bd16260f0a4176daf Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Mon, 11 Aug 2025 16:36:51 +0200 Subject: [PATCH 45/46] Add venv to vocabulary --- doc/styles/config/vocabularies/ANSYS/accept.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/styles/config/vocabularies/ANSYS/accept.txt b/doc/styles/config/vocabularies/ANSYS/accept.txt index 74f841bab..5a3f700d4 100644 --- a/doc/styles/config/vocabularies/ANSYS/accept.txt +++ b/doc/styles/config/vocabularies/ANSYS/accept.txt @@ -17,5 +17,6 @@ postprocess pytest subpackage uv +venv von Wintermantel \ No newline at end of file From 592b8eebef9ba87120f94be0553b6e6a5cdd7a8b Mon Sep 17 00:00:00 2001 From: Pascal Engeler Date: Tue, 12 Aug 2025 11:00:25 +0200 Subject: [PATCH 46/46] Add indentation to trigger code block --- doc/source/examples/ex_python_uv.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/examples/ex_python_uv.rst b/doc/source/examples/ex_python_uv.rst index 7bca36955..2f142674c 100644 --- a/doc/source/examples/ex_python_uv.rst +++ b/doc/source/examples/ex_python_uv.rst @@ -102,7 +102,7 @@ Run the example To run the example, execute the ``project_setup.py`` script:: -uv run project_setup.py + uv run project_setup.py This command sets up a project with a number of jobs. Each job generates a ``plot.png`` file.