Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .github/workflows/ci_cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ on:
- 'v1.0.2'
- 'v1.1.1'
- 'v1.2.0'
- 'v1.3.45'
- 'latest-dev'
hps-feature:
description: HPS feature to test against
Expand Down Expand Up @@ -59,6 +60,21 @@ jobs:
hps-version: ${{ inputs.hps-version || 'latest-dev' }}
hps-feature: ${{ inputs.hps-feature || 'main' }}

backward-compatibility-tests:
strategy:
matrix:
hps-version: ['v1.2.0']
fail-fast: false
uses: ./.github/workflows/tests.yml
secrets: inherit
with:
python-version: '3.10'
toxenv: 'py310'
runner: 'ubuntu-latest'
hps-version: ${{ matrix.hps-version }}
hps-feature: 'main'
upload-coverage: false

docs:
name: Documentation
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ on:
- 'v1.0.2'
- 'v1.1.1'
- 'v1.2.0'
- 'v1.3.45'
- 'latest-dev'

schedule:
Expand Down
8 changes: 6 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ on:
docker-compose-profiles:
description: Docker compose profiles to use
type: string
upload-coverage:
description: Whether to upload coverage results
type: boolean
default: true

jobs:

Expand Down Expand Up @@ -70,15 +74,15 @@ jobs:

- name: Upload coverage results
uses: actions/upload-artifact@v4
if: ${{ inputs.python-version == '3.10' }}
if: ${{ inputs.python-version == '3.10' && inputs.upload-coverage }}
with:
name: coverage-html
path: .cov/html
retention-days: 7

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
if: ${{ inputs.python-version == '3.10' }}
if: ${{ inputs.python-version == '3.10' && inputs.upload-coverage }}
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ repos:
rev: v0.12.7
hooks:
# Run the linter.
- id: ruff
- id: ruff-check
args: [ --fix ]
# Run the formatter.
- id: ruff-format
Expand Down
58 changes: 46 additions & 12 deletions doc/source/getting_started/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -66,20 +66,54 @@ Compatibility with HPS releases

The following table summarizes the compatibility between PyHPS versions and HPS releases.

+------------------------------+-------------------------------+-------------------------------+------------------------------+
| PyHPS version / HPS release | ``1.0.2`` | ``1.1.1`` | ``1.2.0`` |
+==============================+===============================+===============================+==============================+
| ``0.7.X`` | :octicon:`check-circle-fill` | :octicon:`check-circle-fill` | :octicon:`check-circle-fill` |
+------------------------------+-------------------------------+-------------------------------+------------------------------+
| ``0.8.X`` | :octicon:`check-circle-fill` | :octicon:`check-circle-fill` | :octicon:`check-circle-fill` |
+------------------------------+-------------------------------+-------------------------------+------------------------------+
| ``0.9.X`` | :octicon:`check-circle` | :octicon:`check-circle-fill` | :octicon:`check-circle-fill` |
+------------------------------+-------------------------------+-------------------------------+------------------------------+
| ``0.10.X`` | :octicon:`x` | :octicon:`x` | :octicon:`check-circle-fill` |
+------------------------------+-------------------------------+-------------------------------+------------------------------+
.. list-table::
:header-rows: 1
:widths: 40 15 15 15 15

* - PyHPS version / HPS release
- ``1.0.2``
- ``1.1.1``
- ``1.2.0``
- ``1.3.45``
* - ``0.7.X``
- :octicon:`check-circle-fill`
- :octicon:`check-circle-fill`
- :octicon:`check-circle-fill`
- :octicon:`check-circle-fill` `[1] <#note-1>`__
* - ``0.8.X``
- :octicon:`check-circle-fill`
- :octicon:`check-circle-fill`
- :octicon:`check-circle-fill`
- :octicon:`check-circle-fill` `[1] <#note-1>`__
* - ``0.9.X``
- :octicon:`check-circle`
- :octicon:`check-circle-fill`
- :octicon:`check-circle-fill`
- :octicon:`check-circle-fill` `[1] <#note-1>`__
* - ``0.10.X``
- :octicon:`x`
- :octicon:`x`
- :octicon:`check-circle-fill`
- :octicon:`check-circle-fill` `[1] <#note-1>`__
* - ``0.11.X``
- :octicon:`x`
- :octicon:`x`
- :octicon:`check-circle-fill` `[2] <#note-2>`__
- :octicon:`check-circle-fill`


Legend:

- :octicon:`check-circle-fill` Compatible
- :octicon:`check-circle` Backward compatible (new features exposed in PyHPS may not be available in older HPS releases)
- :octicon:`x` Incompatible
- :octicon:`x` Incompatible

.. _note-1:

**[1]** HPS 1.3.45 introduces breaking changes to the schema for task definition templates.
When using PyHPS versions < 0.11.X, functionalities related to task definition templates may not work correctly.
See the HPS 1.3.45 release notes for more information on the changes introduced in this version.

.. _note-2:

**[2]** In PyHPS 0.11.X, functionalities related to task definition templates are only compatible with HPS 1.3.45 and later.
2 changes: 1 addition & 1 deletion src/ansys/hps/client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from .auth import AuthApi
from .authenticate import authenticate, determine_auth_url
from .client import Client
from .exceptions import APIError, ClientError, HPSError
from .exceptions import APIError, ClientError, HPSError, VersionCompatibilityError
from .jms import JmsApi, ProjectApi
from .rms import RmsApi
from .warnings import UnverifiedHTTPSRequestsWarning
3 changes: 3 additions & 0 deletions src/ansys/hps/client/check_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,15 @@ class HpsRelease(Enum):
v1_0_2 = "1.0.2"
v1_1_1 = "1.1.1"
v1_2_0 = "1.2.0"
v1_3_45 = "1.3.45"


"""HPS to JMS version mapping."""
JMS_VERSIONS: dict[HpsRelease, str] = {
HpsRelease.v1_0_2: "1.0.12",
HpsRelease.v1_1_1: "1.0.20",
HpsRelease.v1_2_0: "1.1.4",
HpsRelease.v1_3_45: "1.1.60",
}


Expand All @@ -55,6 +57,7 @@ class HpsRelease(Enum):
HpsRelease.v1_0_2: "1.0.0",
HpsRelease.v1_1_1: "1.1.5",
HpsRelease.v1_2_0: "1.1.10",
HpsRelease.v1_3_45: "1.1.71",
}


Expand Down
5 changes: 5 additions & 0 deletions src/ansys/hps/client/jms/api/jms_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ def restore_project(self, path: str) -> Project:

################################################################
# Task Definition Templates

@version_required(min_version=JMS_VERSIONS[HpsRelease.v1_3_45])
def get_task_definition_templates(
self, as_objects=True, **query_params
) -> list[TaskDefinitionTemplate]:
Expand All @@ -148,6 +150,7 @@ def get_task_definition_templates(
self.client.session, self.url, TaskDefinitionTemplate, as_objects, **query_params
)

@version_required(min_version=JMS_VERSIONS[HpsRelease.v1_3_45])
def create_task_definition_templates(
self, templates: list[TaskDefinitionTemplate], as_objects=True, **query_params
) -> list[TaskDefinitionTemplate]:
Expand All @@ -161,6 +164,7 @@ def create_task_definition_templates(
**query_params,
)

@version_required(min_version=JMS_VERSIONS[HpsRelease.v1_3_45])
def update_task_definition_templates(
self, templates: list[TaskDefinitionTemplate], as_objects=True, **query_params
) -> list[TaskDefinitionTemplate]:
Expand All @@ -178,6 +182,7 @@ def delete_task_definition_templates(self, templates: list[TaskDefinitionTemplat
"""Delete task definition templates."""
return delete_objects(self.client.session, self.url, templates, TaskDefinitionTemplate)

@version_required(min_version=JMS_VERSIONS[HpsRelease.v1_3_45])
def copy_task_definition_templates(
self, templates: list[TaskDefinitionTemplate], wait: bool = True
) -> str | list[str]:
Expand Down
25 changes: 22 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
import pytest
from keycloak import KeycloakAdmin

from ansys.hps.client import AuthApi, Client, JmsApi
from ansys.hps.client import AuthApi, Client, JmsApi, VersionCompatibilityError
from ansys.hps.client.check_version import JMS_VERSIONS, HpsRelease, check_min_version
from ansys.hps.client.jms.resource import Project


Expand Down Expand Up @@ -60,6 +61,11 @@ def client(url, username, password):
return Client(url, username, password, verify=False)


@pytest.fixture
def jms_api(client):
return JmsApi(client)


@pytest.fixture
def keycloak_client(client: Client, keycloak_username, keycloak_password):
keycloak_client = KeycloakAdmin(
Expand Down Expand Up @@ -113,8 +119,21 @@ def build_info_path():


@pytest.fixture
def inactive_temporary_project(client):
jms_api = JmsApi(client)
def inactive_temporary_project(jms_api):
proj = jms_api.create_project(Project(name="pyhps_temporary_project", active=False))
yield proj
jms_api.delete_project(proj)


def xfail_for_hps_version_under(hps_version: HpsRelease, jsm_api: JmsApi, request):
if not check_min_version(jsm_api.version, JMS_VERSIONS[hps_version]):
mark = pytest.mark.xfail(
reason=f"Requires HPS version greater than or equal to {hps_version}",
raises=VersionCompatibilityError,
)
request.node.add_marker(mark)


@pytest.fixture
def has_hps_version_ge_1_3_45(jms_api) -> bool:
return check_min_version(jms_api.version, JMS_VERSIONS[HpsRelease.v1_3_45])
11 changes: 6 additions & 5 deletions tests/jms/test_jms_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ def test_storage_configuration(client):
assert "obj_type" in storage


def test_objects_type_check(client):
def test_objects_type_check(client, has_hps_version_ge_1_3_45):
proj_name = "test_objects_type_check"

proj = Project(name=proj_name, active=True)
Expand All @@ -177,10 +177,11 @@ def test_objects_type_check(client):

jms_api = JmsApi(client)

with pytest.raises(ClientError) as ex_info:
_ = jms_api.create_task_definition_templates([job])
assert "Wrong object type" in str(ex_info.value)
assert "got <class 'ansys.hps.client.jms.resource.job.Job'>" in str(ex_info.value)
if has_hps_version_ge_1_3_45:
with pytest.raises(ClientError) as ex_info:
_ = jms_api.create_task_definition_templates([job])
assert "Wrong object type" in str(ex_info.value)
assert "got <class 'ansys.hps.client.jms.resource.job.Job'>" in str(ex_info.value)

proj = jms_api.create_project(proj, replace=True)
project_api = ProjectApi(client, proj.id)
Expand Down
7 changes: 5 additions & 2 deletions tests/jms/test_job_definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def test_job_definition_delete(client):
jms_api.delete_project(proj)


def test_task_definition_fields(client):
def test_task_definition_fields(client, has_hps_version_ge_1_3_45):
# verify that:
# - store_output is defaulted to True when undefined,
# - memory and disk_space are correctly stored in bytes
Expand Down Expand Up @@ -97,7 +97,10 @@ def test_task_definition_fields(client):
assert task_def.resource_requirements.compute_resource_set_id == "abc123"
assert task_def.modified_by is not missing
assert task_def.created_by is not missing
assert task_def.debug

if has_hps_version_ge_1_3_45:
assert task_def.debug

assert auth_api.get_user(id=task_def.created_by).username == client.username
assert auth_api.get_user(id=task_def.modified_by).username == client.username

Expand Down
22 changes: 15 additions & 7 deletions tests/jms/test_task_definition_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

from ansys.hps.client import HPSError
from ansys.hps.client.auth import AuthApi
from ansys.hps.client.check_version import HpsRelease
from ansys.hps.client.jms import JmsApi
from ansys.hps.client.jms.resource import (
HpcResources,
Expand All @@ -38,6 +39,8 @@
from ansys.hps.client.jms.schema.task_definition_template import TaskDefinitionTemplateSchema
from tests.utils import create_new_user_client, delete_user

from ..conftest import xfail_for_hps_version_under

log = logging.getLogger(__name__)


Expand Down Expand Up @@ -122,8 +125,8 @@ def test_template_deserialization():
assert template.execution_context["my_new_field"].default == "value"


def test_template_integration(client):
jms_api = JmsApi(client)
def test_template_integration(jms_api, request):
xfail_for_hps_version_under(HpsRelease.v1_3_45, jms_api, request)

# Test get queries
templates = jms_api.get_task_definition_templates()
Expand Down Expand Up @@ -197,9 +200,11 @@ def test_template_integration(client):
jms_api.delete_task_definition_templates([new_template])


def test_template_permissions(client, keycloak_client, is_admin):
def test_template_permissions(client, keycloak_client, is_admin, request):
jms_api = JmsApi(client)

xfail_for_hps_version_under(HpsRelease.v1_3_45, jms_api, request)

templates = jms_api.get_task_definition_templates()

# a regular deployment should have some default templates
Expand Down Expand Up @@ -292,8 +297,8 @@ def test_template_permissions(client, keycloak_client, is_admin):
delete_user(keycloak_client, user1)


def test_template_permissions_update(client):
jms_api = JmsApi(client)
def test_template_permissions_update(jms_api, request):
xfail_for_hps_version_under(HpsRelease.v1_3_45, jms_api, request)

# create new template and check default permissions
template = TaskDefinitionTemplate(name="my_template", version=uuid.uuid4())
Expand All @@ -317,8 +322,9 @@ def test_template_permissions_update(client):
jms_api.delete_task_definition_templates([template])


def test_template_anyone_permission(client, keycloak_client):
def test_template_anyone_permission(client, keycloak_client, request):
jms_api = JmsApi(client)
xfail_for_hps_version_under(HpsRelease.v1_3_45, jms_api, request)

# create new template and check default permissions
template = TaskDefinitionTemplate(name="my_template", version=uuid.uuid4())
Expand Down Expand Up @@ -380,7 +386,9 @@ def test_template_anyone_permission(client, keycloak_client):
delete_user(keycloak_client, user1)


def test_template_delete(client, keycloak_client):
def test_template_delete(client, keycloak_client, request):
xfail_for_hps_version_under(HpsRelease.v1_3_45, JmsApi(client), request)

auth_api = AuthApi(client)

# create 2 non-admin users
Expand Down
7 changes: 6 additions & 1 deletion tests/test_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

import logging

import pytest

from ansys.hps.client import __ansys_apps_version__ as ansys_version
from ansys.hps.client.jms import (
IntParameterDefinition,
Expand Down Expand Up @@ -141,7 +143,10 @@ def test_python_two_bar_truss_problem_with_exec_script(client):
jms_api.delete_project(project)


def test_python_two_bar_truss_params_in_exec_script(client):
def test_python_two_bar_truss_params_in_exec_script(client, has_hps_version_ge_1_3_45):
if not has_hps_version_ge_1_3_45:
pytest.skip("Returning output parameters in the execution script was added in HPS 1.3.45.")

from examples.python_two_bar_truss_params_in_exec_script.project_setup import main

num_jobs = 10
Expand Down
Loading