diff --git a/src/ansys/dpf/core/operator_specification.py b/src/ansys/dpf/core/operator_specification.py index 1bbe5aaaad..60da80eee2 100644 --- a/src/ansys/dpf/core/operator_specification.py +++ b/src/ansys/dpf/core/operator_specification.py @@ -29,7 +29,7 @@ from __future__ import annotations import abc -from typing import Union +from typing import Union, Tuple from ansys.dpf.core import common, mapping_types, server as server_module from ansys.dpf.core.check_version import server_meet_version, version_requires @@ -37,8 +37,9 @@ integral_types, operator_specification_capi, operator_specification_grpcapi, + semantic_version_capi ) - +import ctypes class PinSpecification: """Documents an input or output pin of an Operator. @@ -496,6 +497,23 @@ def config_specification(self) -> ConfigSpecification: document=option_doc, ) return self._config_specification + + @property + def version(self) -> str: + semver_obj = lambda: None + semver_obj._internal_obj = self._api.operator_specification_get_version(self) + + semver_api = self._server.get_api_for_type( + capi=semantic_version_capi.SemanticVersionCAPI, grpcapi=None + ) + + major = ctypes.c_uint16(0) + minor = ctypes.c_uint16(0) + patch = ctypes.c_uint16(0) + semver_api.semantic_vesion_get_components(semver_obj, ctypes.byref(major), ctypes.byref(minor), ctypes.byref(patch)) + + return f"{major}.{minor}.{patch}" + class CustomConfigOptionSpec(ConfigOptionSpec): @@ -866,3 +884,31 @@ def properties(self, val: SpecificationProperties): for key, value in val.items(): if value is not None: self._api.operator_specification_set_property(self, key, value) + + @property + def version(self) -> str: + return super().version + + @version.setter + def version(self, ver_obj: Union[Tuple[int], Tuple[int, int], Tuple[int, int, int]]): + major = 0 + minor = 0 + patch = 0 + if isinstance(ver_obj, tuple): + if len(ver_obj) > 0 : + major = ver_obj[0] + if len(ver_obj) > 1: + minor = ver_obj[1] + if len(ver_obj) > 2: + patch = ver_obj[2] + + semver_api: semantic_version_capi.semantic_version_abstract_api.SemanticVersionAbstractAPI = self._server.get_api_for_type( + capi=semantic_version_capi.SemanticVersionCAPI, + grpcapi=None + ) + + # proxy obj + semver_obj = lambda: None + semver_obj._internal_obj = semver_api.semantic_version_new(major, minor, patch) + + self._api.operator_specification_set_version(self, semver_obj) diff --git a/tests/test_python_plugins.py b/tests/test_python_plugins.py index a13892612f..f38e1f411b 100644 --- a/tests/test_python_plugins.py +++ b/tests/test_python_plugins.py @@ -35,6 +35,7 @@ CustomSpecification, PinSpecification, SpecificationProperties, + Specification ) import conftest from conftest import ( @@ -408,3 +409,26 @@ def test_custom_op_with_spec(server_type_remote_process, testfiles_dir): outf = op.outputs.field() expected = np.ones((3, 3), dtype=np.float64) + 4.0 assert np.allclose(outf.data, expected) + + +def test_custom_op_without_version(testfiles_dir): + dpf.load_library( + dpf.path_utilities.to_server_os( + Path(testfiles_dir) / "pythonPlugins" + ), + "py_operator_with_spec", + "load_operators", + ) + spec = Specification("custom_add_to_field") + assert spec.version == "0.0.0" + +def test_custom_op_with_version(testfiles_dir): + dpf.load_library( + dpf.path_utilities.to_server_os( + Path(testfiles_dir) / "pythonPlugins", + ), + "py_operator_with_spec", + "load_operators" + ) + spec = Specification("__op_with_version") + assert spec.version == "2.3.1" \ No newline at end of file diff --git a/tests/testfiles/pythonPlugins/operator_with_spec.py b/tests/testfiles/pythonPlugins/operator_with_spec.py index 4743ff0ae4..4f8b1219e4 100644 --- a/tests/testfiles/pythonPlugins/operator_with_spec.py +++ b/tests/testfiles/pythonPlugins/operator_with_spec.py @@ -56,6 +56,24 @@ def specification(self): def name(self): return "custom_add_to_field" +class OpWithVersion(CustomOperatorBase): + def run(self): + self.set_output("The brown fox jumps over the lazy dog.") + self.set_succeeded() + + @property + def specification(self): + spec = CustomSpecification() + spec.description = "Outputs a string" + spec.outputs = { + 0: PinSpecification("message", [str], "Important message") + } + spec.version = (2, 3, 1) + return spec + @property + def name(self): + return "__op_with_version" def load_operators(*args): record_operator(AddFloatToFieldData, *args) + record_operator(OpWithVersion, *args)