Skip to content

Commit 4c5a2b1

Browse files
committed
Expose changelog in custom operators
1 parent d1960d0 commit 4c5a2b1

File tree

6 files changed

+142
-5
lines changed

6 files changed

+142
-5
lines changed

src/ansys/dpf/core/changelog.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
from ansys.dpf.core.check_version import version_requires
3131

3232

33-
@version_requires("11.0")
3433
class Changelog:
3534
"""Changelog of an operator.
3635
@@ -55,6 +54,7 @@ def __init__(self, gdc: dpf.GenericDataContainer = None, server=None):
5554
gdc.set_property(property_name="changes", prop=changes_sf)
5655
gdc.set_property(property_name="class", prop="Changelog")
5756
self.gdc = gdc
57+
self._server = server
5858

5959
def append(self, version: Version, changes: str):
6060
"""Append a version and associated changes description to the changelog."""
@@ -124,7 +124,7 @@ def major_bump(self, changes: str) -> Changelog:
124124
self.append(version=new_version, changes=changes)
125125
return self
126126

127-
def expect_version(self, version: Version) -> Changelog:
127+
def expect_version(self, version: Version | str) -> Changelog:
128128
"""Check the current latest version of the changelog.
129129
130130
Useful when chaining version bumps to check the resulting version is as expected.
@@ -140,6 +140,8 @@ def expect_version(self, version: Version) -> Changelog:
140140
changelog:
141141
Returns the current changelog to allow for chaining calls to bumps.
142142
"""
143+
if isinstance(version, str):
144+
version = Version(version)
143145
if self.last_version != version:
144146
raise ValueError(
145147
f"Last version in the changelog ({self.last_version}) does not match expected version ({version})."

src/ansys/dpf/core/custom_operator.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import zipfile
3838

3939
import numpy
40+
from packaging.version import Version
4041

4142
from ansys.dpf import core as dpf
4243
from ansys.dpf.core import (
@@ -55,6 +56,8 @@
5556
external_operator_api,
5657
functions_registry,
5758
)
59+
from ansys.dpf.core.changelog import Changelog
60+
from ansys.dpf.core.check_version import version_requires
5861
from ansys.dpf.gate import capi, dpf_vector, integral_types, object_handler
5962

6063

@@ -400,3 +403,39 @@ def name(self) -> str:
400403
This name can then be used to instantiate the Operator.
401404
"""
402405
pass
406+
407+
@property
408+
@version_requires("11.0")
409+
def changelog(self) -> Changelog:
410+
"""Return the changelog of this operator.
411+
412+
Requires DPF 11.0 (2026 R1) or above.
413+
414+
Returns
415+
-------
416+
changelog:
417+
Changelog of the operator.
418+
"""
419+
from ansys.dpf.core.operators.utility.operator_changelog import operator_changelog
420+
421+
return Changelog(operator_changelog(operator_name=self.name).eval())
422+
423+
@changelog.setter
424+
@version_requires("11.0")
425+
def changelog(self, changelog: Changelog):
426+
"""Set the changelog of this operator.
427+
428+
Requires DPF 11.0 (2026 R1) or above.
429+
430+
"""
431+
self.specification.set_changelog(changelog)
432+
433+
@property
434+
@version_requires("11.0")
435+
def version(self) -> Version:
436+
"""Return the current version of the operator based on its changelog.
437+
438+
Requires DPF 11.0 (2026 R1) or above.
439+
440+
"""
441+
return self.changelog.last_version

src/ansys/dpf/core/dpf_operator.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -933,8 +933,8 @@ def specification(self):
933933
else:
934934
return Specification(operator_name=self.name, server=self._server)
935935

936-
@version_requires("11.0")
937936
@property
937+
@version_requires("11.0")
938938
def changelog(self) -> Changelog:
939939
"""Return the changelog of this operator.
940940
@@ -947,10 +947,13 @@ def changelog(self) -> Changelog:
947947
"""
948948
from ansys.dpf.core.operators.utility.operator_changelog import operator_changelog
949949

950-
return Changelog(operator_changelog(operator_name=self.name, server=self._server).eval())
950+
return Changelog(
951+
gdc=operator_changelog(operator_name=self.name, server=self._server).eval(),
952+
server=self._server,
953+
)
951954

952-
@version_requires("11.0")
953955
@property
956+
@version_requires("11.0")
954957
def version(self) -> Version:
955958
"""Return the current version of the operator.
956959

src/ansys/dpf/core/operator_specification.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from typing import Union
3333

3434
from ansys.dpf.core import common, mapping_types, server as server_module
35+
from ansys.dpf.core.changelog import Changelog
3536
from ansys.dpf.core.check_version import server_meet_version, version_requires
3637
from ansys.dpf.gate import (
3738
integral_types,
@@ -497,6 +498,15 @@ def config_specification(self) -> ConfigSpecification:
497498
)
498499
return self._config_specification
499500

501+
@version_requires("11.0")
502+
def set_changelog(self, changelog: Changelog):
503+
"""Set the changelog for this operator specification.
504+
505+
Requires DPF 11.0 (2026 R1) or above.
506+
507+
"""
508+
self._api.operator_specification_set_changelog(self, changelog.gdc)
509+
500510

501511
class CustomConfigOptionSpec(ConfigOptionSpec):
502512
"""Custom documentation of a configuration option available for a given operator."""

tests/test_python_plugins.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import pytest
2929

3030
from ansys.dpf import core as dpf
31+
from ansys.dpf.core.changelog import Changelog
3132
from ansys.dpf.core.custom_operator import update_virtual_environment_for_custom_operators
3233
from ansys.dpf.core.errors import DPFServerException
3334
from ansys.dpf.core.operator_specification import (
@@ -408,3 +409,22 @@ def test_custom_op_with_spec(server_type_remote_process, testfiles_dir):
408409
outf = op.outputs.field()
409410
expected = np.ones((3, 3), dtype=np.float64) + 4.0
410411
assert np.allclose(outf.data, expected)
412+
413+
414+
@conftest.raises_for_servers_version_under("11.0")
415+
def test_custom_op_changelog(server_type_remote_process, testfiles_dir):
416+
from packaging.version import Version
417+
418+
dpf.load_library(
419+
dpf.path_utilities.to_server_os(
420+
Path(testfiles_dir) / "pythonPlugins", server_type_remote_process
421+
),
422+
"py_operator_with_changelog",
423+
"load_operators",
424+
server=server_type_remote_process,
425+
)
426+
op = dpf.Operator("custom_add_to_field", server=server_type_remote_process)
427+
changelog = op.changelog
428+
assert isinstance(changelog, Changelog)
429+
assert changelog.last_version == Version("1.0.0")
430+
assert changelog[Version("1.0.0")] == "Major bump"
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Copyright (C) 2020 - 2025 ANSYS, Inc. and/or its affiliates.
2+
# SPDX-License-Identifier: MIT
3+
#
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in all
13+
# copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
# SOFTWARE.
22+
23+
from ansys.dpf.core import Field
24+
from ansys.dpf.core.changelog import Changelog
25+
from ansys.dpf.core.custom_operator import CustomOperatorBase, record_operator
26+
from ansys.dpf.core.operator_specification import (
27+
CustomSpecification,
28+
PinSpecification,
29+
SpecificationProperties,
30+
)
31+
32+
33+
class AddFloatToFieldData(CustomOperatorBase):
34+
def run(self):
35+
field = self.get_input(0, Field)
36+
to_add = self.get_input(1, float)
37+
data = field.data
38+
data += to_add
39+
self.set_output(0, field)
40+
self.set_succeeded()
41+
42+
@property
43+
def specification(self):
44+
spec = CustomSpecification()
45+
spec.description = "Add a custom value to all the data of an input Field"
46+
spec.inputs = {
47+
0: PinSpecification("field", [Field], "Field on which float value is added."),
48+
1: PinSpecification("to_add", [float], "Data to add."),
49+
}
50+
spec.outputs = {
51+
0: PinSpecification("field", [Field], "Field on which the float value is added.")
52+
}
53+
spec.properties = SpecificationProperties("custom add to field", "math")
54+
spec.set_changelog(changelog=Changelog().major_bump("Major bump").expect_version("1.0.0"))
55+
return spec
56+
57+
@property
58+
def name(self):
59+
return "custom_add_to_field"
60+
61+
62+
def load_operators(*args):
63+
record_operator(AddFloatToFieldData, *args)

0 commit comments

Comments
 (0)