diff --git a/doc/changelog.d/1882.added.md b/doc/changelog.d/1882.added.md new file mode 100644 index 0000000000..1dc887843e --- /dev/null +++ b/doc/changelog.d/1882.added.md @@ -0,0 +1 @@ +DbuApplication stub relocation \ No newline at end of file diff --git a/src/ansys/geometry/core/_grpc/_services/_service.py b/src/ansys/geometry/core/_grpc/_services/_service.py index d360c4b1bb..a3e2654877 100644 --- a/src/ansys/geometry/core/_grpc/_services/_service.py +++ b/src/ansys/geometry/core/_grpc/_services/_service.py @@ -23,7 +23,9 @@ import grpc from .._version import GeometryApiProtos, set_proto_version +from .base.admin import GRPCAdminService from .base.bodies import GRPCBodyService +from .base.dbuapplication import GRPCDbuApplicationService class _GRPCServices: @@ -66,6 +68,7 @@ def __init__(self, channel: grpc.Channel, version: GeometryApiProtos | str | Non # Lazy load all the services self._admin = None self._bodies = None + self._dbu_application = None @property def bodies(self) -> GRPCBodyService: @@ -84,15 +87,17 @@ def bodies(self) -> GRPCBodyService: if self.version == GeometryApiProtos.V0: self._bodies = GRPCBodyServiceV0(self.channel) - elif self.version == GeometryApiProtos.V1: + elif self.version == GeometryApiProtos.V1: # pragma: no cover + # V1 is not implemented yet self._bodies = GRPCBodyServiceV1(self.channel) - else: + else: # pragma: no cover + # This should never happen as the version is set in the constructor raise ValueError(f"Unsupported version: {self.version}") return self._bodies @property - def admin(self): + def admin(self) -> GRPCAdminService: """ Get the admin service for the specified version. @@ -108,9 +113,37 @@ def admin(self): if self.version == GeometryApiProtos.V0: self._admin = GRPCAdminServiceV0(self.channel) - elif self.version == GeometryApiProtos.V1: + elif self.version == GeometryApiProtos.V1: # pragma: no cover + # V1 is not implemented yet self._admin = GRPCAdminServiceV1(self.channel) - else: + else: # pragma: no cover + # This should never happen as the version is set in the constructor raise ValueError(f"Unsupported version: {self.version}") return self._admin + + @property + def dbu_application(self) -> GRPCDbuApplicationService: + """ + Get the DBU application service for the specified version. + + Returns + ------- + DbuApplicationServiceBase + The DBU application service for the specified version. + """ + if not self._dbu_application: + # Import the appropriate DBU application service based on the version + from .v0.dbuapplication import GRPCDbuApplicationServiceV0 + from .v1.dbuapplication import GRPCDbuApplicationServiceV1 + + if self.version == GeometryApiProtos.V0: + self._dbu_application = GRPCDbuApplicationServiceV0(self.channel) + elif self.version == GeometryApiProtos.V1: # pragma: no cover + # V1 is not implemented yet + self._dbu_application = GRPCDbuApplicationServiceV1(self.channel) + else: # pragma: no cover + # This should never happen as the version is set in the constructor + raise ValueError(f"Unsupported version: {self.version}") + + return self._dbu_application diff --git a/src/ansys/geometry/core/_grpc/_services/base/dbuapplication.py b/src/ansys/geometry/core/_grpc/_services/base/dbuapplication.py new file mode 100644 index 0000000000..a8b366f8b2 --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/base/dbuapplication.py @@ -0,0 +1,45 @@ +# Copyright (C) 2023 - 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. +"""Module containing the DBU Application service implementation (abstraction layer).""" + +from abc import ABC, abstractmethod + +import grpc + + +class GRPCDbuApplicationService(ABC): + """DBU Application service for gRPC communication with the Geometry server. + + Parameters + ---------- + channel : grpc.Channel + The gRPC channel to the server. + """ + + def __init__(self, channel: grpc.Channel): + """Initialize the GRPCDbuApplicationService class.""" + pass # pragma: no cover + + @abstractmethod + def run_script(self, **kwargs) -> dict: + """Run a Scripting API script.""" + pass # pragma: no cover diff --git a/src/ansys/geometry/core/_grpc/_services/v0/admin.py b/src/ansys/geometry/core/_grpc/_services/v0/admin.py index a76f6d16b6..1b0f2afc8f 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/admin.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/admin.py @@ -45,8 +45,8 @@ class GRPCAdminServiceV0(GRPCAdminService): The gRPC channel to the server. """ - def __init__(self, channel: grpc.Channel): - """Initialize the AdminServiceBase class.""" + @protect_grpc + def __init__(self, channel: grpc.Channel): # noqa: D102 from ansys.api.dbu.v0.admin_pb2_grpc import AdminStub self.stub = AdminStub(channel) diff --git a/src/ansys/geometry/core/_grpc/_services/v0/bodies.py b/src/ansys/geometry/core/_grpc/_services/v0/bodies.py index 3ef929503d..53d5b7b3a6 100644 --- a/src/ansys/geometry/core/_grpc/_services/v0/bodies.py +++ b/src/ansys/geometry/core/_grpc/_services/v0/bodies.py @@ -59,8 +59,7 @@ class GRPCBodyServiceV0(GRPCBodyService): """ @protect_grpc - def __init__(self, channel: grpc.Channel): - """Initialize the BodyService with the gRPC stub.""" + def __init__(self, channel: grpc.Channel): # noqa: D102 from ansys.api.geometry.v0.bodies_pb2_grpc import BodiesStub self.stub = BodiesStub(channel) diff --git a/src/ansys/geometry/core/_grpc/_services/v0/dbuapplication.py b/src/ansys/geometry/core/_grpc/_services/v0/dbuapplication.py new file mode 100644 index 0000000000..6e35bdb9db --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/v0/dbuapplication.py @@ -0,0 +1,69 @@ +# Copyright (C) 2023 - 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. +"""Module containing the DBU Application service implementation for v0.""" + +import grpc + +from ansys.geometry.core.errors import protect_grpc + +from ..base.dbuapplication import GRPCDbuApplicationService + + +class GRPCDbuApplicationServiceV0(GRPCDbuApplicationService): + """DBU Application service for gRPC communication with the Geometry server. + + This class provides methods to interact with the Geometry server's + DBU Application service. It is specifically designed for the v0 version + of the Geometry API. + + Parameters + ---------- + channel : grpc.Channel + The gRPC channel to the server. + """ + + @protect_grpc + def __init__(self, channel: grpc.Channel): # noqa: D102 + from ansys.api.dbu.v0.dbuapplication_pb2_grpc import DbuApplicationStub + + self.stub = DbuApplicationStub(channel) + + @protect_grpc + def run_script(self, **kwargs) -> dict: # noqa: D102 + from ansys.api.dbu.v0.dbuapplication_pb2 import RunScriptFileRequest + + # Create the request - assumes all inputs are valid and of the proper type + request = RunScriptFileRequest( + script_path=kwargs["script_path"], + script_args=kwargs["script_args"], + api_version=kwargs["api_version"], + ) + + # Call the gRPC service + response = self.stub.RunScriptFile(request) + + # Return the response - formatted as a dictionary + return { + "success": response.success, + "message": response.message, + "values": None if not response.values else dict(response.values), + } diff --git a/src/ansys/geometry/core/_grpc/_services/v1/admin.py b/src/ansys/geometry/core/_grpc/_services/v1/admin.py index a7c1c23a36..3f4890d0eb 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/admin.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/admin.py @@ -23,6 +23,8 @@ import grpc +from ansys.geometry.core.errors import protect_grpc + from ..base.admin import GRPCAdminService @@ -39,14 +41,16 @@ class GRPCAdminServiceV1(GRPCAdminService): # pragma: no cover The gRPC channel to the server. """ - def __init__(self, channel: grpc.Channel): - """Initialize the AdminServiceBase class.""" + @protect_grpc + def __init__(self, channel: grpc.Channel): # noqa: D102 from ansys.api.dbu.v1.admin_pb2_grpc import AdminStub self.stub = AdminStub(channel) + @protect_grpc def get_backend(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def get_logs(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError diff --git a/src/ansys/geometry/core/_grpc/_services/v1/bodies.py b/src/ansys/geometry/core/_grpc/_services/v1/bodies.py index 5431cbd4d4..46ba67ed5b 100644 --- a/src/ansys/geometry/core/_grpc/_services/v1/bodies.py +++ b/src/ansys/geometry/core/_grpc/_services/v1/bodies.py @@ -48,101 +48,134 @@ def __init__(self, channel: grpc.Channel): self.stub = BodiesStub(channel) + @protect_grpc def create_sphere_body(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def create_extruded_body(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def create_sweeping_profile_body(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def create_sweeping_chain(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def create_extruded_body_from_face_profile(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def create_extruded_body_from_loft_profiles(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def create_planar_body(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def create_body_from_face(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def create_surface_body(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def create_surface_body_from_trimmed_curves(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def translate(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def delete(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def is_suppressed(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def get_color(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def get_faces(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def get_edges(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def get_volume(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def get_bounding_box(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def set_assigned_material(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def get_assigned_material(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def set_name(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def set_fill_style(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def set_suppressed(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def set_color(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def rotate(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def scale(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def mirror(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def map(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def get_collision(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def copy(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def get_tesellation(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def get_tesellation_with_options(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError + @protect_grpc def boolean(self, **kwargs) -> dict: # noqa: D102 raise NotImplementedError diff --git a/src/ansys/geometry/core/_grpc/_services/v1/dbuapplication.py b/src/ansys/geometry/core/_grpc/_services/v1/dbuapplication.py new file mode 100644 index 0000000000..3386a2d5da --- /dev/null +++ b/src/ansys/geometry/core/_grpc/_services/v1/dbuapplication.py @@ -0,0 +1,52 @@ +# Copyright (C) 2023 - 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. +"""Module containing the DBU Application service implementation for v1.""" + +import grpc + +from ansys.geometry.core.errors import protect_grpc + +from ..base.dbuapplication import GRPCDbuApplicationService + + +class GRPCDbuApplicationServiceV1(GRPCDbuApplicationService): # pragma: no cover + """DBU Application service for gRPC communication with the Geometry server. + + This class provides methods to interact with the Geometry server's + DBU Application service. It is specifically designed for the v1 version + of the Geometry API. + + Parameters + ---------- + channel : grpc.Channel + The gRPC channel to the server. + """ + + @protect_grpc + def __init__(self, channel: grpc.Channel): # noqa: D102 + from ansys.api.dbu.v1.dbuapplication_pb2_grpc import DbuApplicationStub + + self.stub = DbuApplicationStub(channel) + + @protect_grpc + def run_script(self, **kwargs) -> dict: # noqa: D102 + raise NotImplementedError diff --git a/src/ansys/geometry/core/modeler.py b/src/ansys/geometry/core/modeler.py index 45dac0cb68..f721858e9d 100644 --- a/src/ansys/geometry/core/modeler.py +++ b/src/ansys/geometry/core/modeler.py @@ -27,8 +27,6 @@ from grpc import Channel -from ansys.api.dbu.v0.dbuapplication_pb2 import RunScriptFileRequest -from ansys.api.dbu.v0.dbuapplication_pb2_grpc import DbuApplicationStub from ansys.api.dbu.v0.designs_pb2 import OpenRequest from ansys.api.dbu.v0.designs_pb2_grpc import DesignsStub from ansys.api.geometry.v0.commands_pb2 import UploadFileRequest @@ -560,25 +558,23 @@ def run_discovery_script_file( api_version = ApiVersions.parse_input(api_version) serv_path = self._upload_file(file_path) - ga_stub = DbuApplicationStub(self.client.channel) - request = RunScriptFileRequest( + + self.client.log.debug(f"Running Discovery script file at {file_path}...") + response = self.client.services.dbu_application.run_script( script_path=serv_path, script_args=script_args, api_version=api_version.value if api_version is not None else None, ) - self.client.log.debug(f"Running Discovery script file at {file_path}...") - response = ga_stub.RunScriptFile(request) - - if not response.success: - raise GeometryRuntimeError(response.message) + if not response["success"]: + raise GeometryRuntimeError(response["message"]) - self.client.log.debug(f"Script result message: {response.message}") + self.client.log.debug(f"Script result message: {response['message']}") if import_design: - return (dict(response.values), self.read_existing_design()) + return response["values"], self.read_existing_design() else: - return dict(response.values) + return response["values"], None @property def repair_tools(self) -> RepairTools: diff --git a/tests/integration/test_runscript.py b/tests/integration/test_runscript.py index 1ba9438593..4acc80c7a3 100644 --- a/tests/integration/test_runscript.py +++ b/tests/integration/test_runscript.py @@ -35,7 +35,7 @@ # Python (.py) def test_python_simple_script(modeler: Modeler): - result = modeler.run_discovery_script_file(DSCOSCRIPTS_FILES_DIR / "simple_script.py") + result, _ = modeler.run_discovery_script_file(DSCOSCRIPTS_FILES_DIR / "simple_script.py") pattern_db = re.compile(r"SpaceClaim\.Api\.[A-Za-z0-9]+\.DesignBody", re.IGNORECASE) pattern_doc = re.compile(r"SpaceClaim\.Api\.[A-Za-z0-9]+\.Document", re.IGNORECASE) assert len(result) == 2 @@ -46,7 +46,7 @@ def test_python_simple_script(modeler: Modeler): def test_python_simple_script_ignore_api_version( modeler: Modeler, caplog: pytest.LogCaptureFixture ): - result = modeler.run_discovery_script_file( + result, _ = modeler.run_discovery_script_file( DSCOSCRIPTS_FILES_DIR / "simple_script.py", api_version=ApiVersions.LATEST, ) @@ -92,7 +92,7 @@ def test_python_integrated_script(modeler: Modeler): # SpaceClaim (.scscript) def test_scscript_simple_script(modeler: Modeler): - result = modeler.run_discovery_script_file(DSCOSCRIPTS_FILES_DIR / "simple_script.scscript") + result, _ = modeler.run_discovery_script_file(DSCOSCRIPTS_FILES_DIR / "simple_script.scscript") assert len(result) == 2 pattern_db = re.compile(r"SpaceClaim\.Api\.[A-Za-z0-9]+\.DesignBody", re.IGNORECASE) pattern_doc = re.compile(r"SpaceClaim\.Api\.[A-Za-z0-9]+\.Document", re.IGNORECASE) @@ -103,7 +103,7 @@ def test_scscript_simple_script(modeler: Modeler): # Discovery (.dscript) def test_dscript_simple_script(modeler: Modeler): - result = modeler.run_discovery_script_file(DSCOSCRIPTS_FILES_DIR / "simple_script.dscript") + result, _ = modeler.run_discovery_script_file(DSCOSCRIPTS_FILES_DIR / "simple_script.dscript") assert len(result) == 2 pattern_db = re.compile(r"SpaceClaim\.Api\.[A-Za-z0-9]+\.DesignBody", re.IGNORECASE) pattern_doc = re.compile(r"SpaceClaim\.Api\.[A-Za-z0-9]+\.Document", re.IGNORECASE)