diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 71e9383e64..7a024613a8 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -39,38 +39,9 @@ jobs: with: token: ${{ secrets.PYANSYS_CI_BOT_TOKEN }} - branch-name: - if: github.event_name == 'pull_request' - name: Check the name of the branch - runs-on: ubuntu-latest - steps: - - name: Check branch name - uses: ansys/actions/branch-name-style@main - - style: - name: Code style - runs-on: ubuntu-latest - steps: - - name: PyAnsys code style checks - uses: ansys/actions/code-style@v6 - with: - python-version: ${{ env.MAIN_PYTHON_VERSION }} - vale-config: "doc/.vale.ini" - vale-version: "3.4.1" - - docs-style: - name: Documentation Style Check - runs-on: ubuntu-latest - steps: - - name: PyAnsys documentation style checks - uses: ansys/actions/doc-style@v6 - with: - token: ${{ secrets.GITHUB_TOKEN }} - smoke-tests: name: Build and Smoke tests runs-on: ${{ matrix.os }} - needs: [style] strategy: fail-fast: false matrix: @@ -93,7 +64,6 @@ jobs: docs: name: Documentation runs-on: ubuntu-latest - needs: [docs-style] steps: - name: Login in Github Container registry @@ -107,10 +77,10 @@ jobs: run: docker pull ${{ env.DOCKER_IMAGE_NAME }}:${{ env.DOCKER_IMAGE_TAG }} - name: Setup headless display - uses: pyvista/setup-headless-display-action@v2 + uses: pyvista/setup-headless-display-action@v4 - name: "Run Ansys documentation building action" - uses: ansys/actions/doc-build@v6 + uses: ansys/actions/doc-build@v10 with: check-links: false env: diff --git a/doc/source/getting_started/index.rst b/doc/source/getting_started/index.rst index ccf8b96a4e..36de93b74e 100644 --- a/doc/source/getting_started/index.rst +++ b/doc/source/getting_started/index.rst @@ -57,6 +57,17 @@ To install a basic version of the client, use this command instead: pip install -e . +Connecting through gRPC +----------------------- + +PyPrimeMesh uses gRPC to provide secure communications between client and server. +When you run the client and server on the same machine: + +- For Linux OS, PyPrimeMesh uses UDS (Unix Domain Socket) for communications. +- For Windows OS, PyPrimeMesh uses interceptor to validate gRPC connections, + ensures the client is running on the same Windows user account as the server and authenticates the client. + +When you launch PyPrimeMesh, gRPC establishes a connection between the Client and the Server with local a connection only. Dependencies ------------ diff --git a/doc/styles/config/vocabularies/ANSYS/accept.txt b/doc/styles/config/vocabularies/ANSYS/accept.txt index 600c223aa2..c59492bb5e 100644 --- a/doc/styles/config/vocabularies/ANSYS/accept.txt +++ b/doc/styles/config/vocabularies/ANSYS/accept.txt @@ -5,6 +5,7 @@ AutoMesh automesh Boolean CAD +client_certs_dir conformally [Dd]efeature defeaturing diff --git a/pyproject.toml b/pyproject.toml index 4535ebb58b..188b2d4307 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "flit_core.buildapi" [project] name = "ansys-meshing-prime" -version = "0.6.2" +version = "0.6.3" description = "PyPrimeMesh is a Python client to Ansys Prime Server, which delivers core Ansys meshing technology." readme = "README.md" requires-python = ">=3.8,<4" @@ -31,20 +31,21 @@ dependencies = [ [project.optional-dependencies] graphics = [ "pyvista[jupyter]>=0.38.1", + "vtk>=9.3.0", ] tests = [ "pytest==8.2.2", "pytest-cov==5.0.0", "pytest-pyvista==0.1.9", "pytest-xvfb==3.0.0", - "pyvista[trame]==0.44.0" + "pyvista[trame]>=0.44.1", ] doc = [ "ansys-sphinx-theme==0.15.2", "jupyter-sphinx==0.5.3", "numpydoc==1.7.0", "Sphinx==7.2.6", - "pyvista==0.44.0", + "pyvista>=0.44.1", "sphinx-autodoc-typehints==2.0.1", "sphinx-copybutton==0.5.2", "sphinx-gallery==0.15.0", diff --git a/src/ansys/meshing/prime/graphics/graphics.py b/src/ansys/meshing/prime/graphics/graphics.py index 6cc9a2c16e..d70d16f071 100644 --- a/src/ansys/meshing/prime/graphics/graphics.py +++ b/src/ansys/meshing/prime/graphics/graphics.py @@ -28,7 +28,7 @@ import numpy as np import pyvista as pv import vtk -from pyvista import Plotter +from pyvista.plotting.plotting import Plotter import ansys.meshing.prime as prime from ansys.meshing.prime.graphics.trame_gui import _HAS_TRAME, TrameVisualizer diff --git a/src/ansys/meshing/prime/internals/client.py b/src/ansys/meshing/prime/internals/client.py index 076ed65fb3..804ae54e9c 100644 --- a/src/ansys/meshing/prime/internals/client.py +++ b/src/ansys/meshing/prime/internals/client.py @@ -65,6 +65,8 @@ def __init__( port: int = defaults.port(), timeout: float = defaults.connection_timeout(), credentials=None, + connection_type: config.ConnectionType = config.ConnectionType.GRPC, + uds_file: str = None, **kwargs, ): """Initialize the client.""" @@ -76,28 +78,37 @@ def __init__( self._process = server_process self._comm = None if not local: - try: - from ansys.meshing.prime.internals.grpc_communicator import ( - GRPCCommunicator, - ) - - channel = kwargs.get('channel', None) - if channel is not None: - self._comm = GRPCCommunicator(channel=channel, timeout=timeout) - else: - self._comm = GRPCCommunicator( - ip=ip, port=port, timeout=timeout, credentials=credentials + if connection_type == config.ConnectionType.GRPC: + try: + from ansys.meshing.prime.internals.grpc_communicator import ( + GRPCCommunicator, ) - setattr(self, 'port', port) - except ImportError as err: - logging.getLogger('PyPrimeMesh').error( - f'Failed to load grpc_communicator with message: {err.msg}' - ) - raise - except ConnectionError: - self.exit() - logging.getLogger('PyPrimeMesh').error('Failed to connect to PRIME GRPC server') + channel = kwargs.get('channel', None) + if channel is not None: + self._comm = GRPCCommunicator(channel=channel, timeout=timeout) + else: + if os.name == 'nt': + self._comm = GRPCCommunicator( + ip=ip, port=port, timeout=timeout, credentials=credentials + ) + else: + self._comm = GRPCCommunicator( + uds_file=uds_file, timeout=timeout, credentials=credentials + ) + setattr(self, 'port', port) + except ImportError as err: + logging.getLogger('PyPrimeMesh').error( + f'Failed to load grpc_communicator with message: {err.msg}' + ) + raise + except ConnectionError: + self.exit() + + logging.getLogger('PyPrimeMesh').error('Failed to connect to PRIME GRPC server') + raise + else: + logging.getLogger('PyPrimeMesh').error(f'Invalid server type: {connection_type}') raise else: try: diff --git a/src/ansys/meshing/prime/internals/config.py b/src/ansys/meshing/prime/internals/config.py index b0692a06b5..284e2aa171 100644 --- a/src/ansys/meshing/prime/internals/config.py +++ b/src/ansys/meshing/prime/internals/config.py @@ -23,6 +23,7 @@ """Configuration utility for PyPrimeMesh.""" from contextlib import contextmanager +from enum import Enum __all__ = [ 'enable_optimizing_numpy_arrays', @@ -45,6 +46,10 @@ from ansys.meshing.prime.internals.logger import PrimeLogger +class ConnectionType(Enum): + GRPC = 1 + + def _optimize_vectors(): """Get the value of the flag for optimizing vectors.""" return __DEFAULT_USE_BINARY diff --git a/src/ansys/meshing/prime/internals/grpc_communicator.py b/src/ansys/meshing/prime/internals/grpc_communicator.py index 0ab1ee4ca7..d4e895efa9 100644 --- a/src/ansys/meshing/prime/internals/grpc_communicator.py +++ b/src/ansys/meshing/prime/internals/grpc_communicator.py @@ -104,7 +104,13 @@ def __init__( ip_addr = f"{ip}:{port}" channel_options = grpc_utils.get_default_channel_args() if credentials is None: - self._channel = grpc.insecure_channel(ip_addr, options=channel_options) + uds_file = kwargs.get('uds_file', None) + if uds_file is not None: + options = (('grpc.default_authority', 'localhost'),) + self._channel = grpc.insecure_channel(uds_file, options=options) + else: + options = (('grpc.default_authority', 'localhost'),) + self._channel = grpc.insecure_channel(ip_addr, options=options) else: self._channel = grpc.secure_channel(ip_addr, credentials, options=channel_options) diff --git a/src/ansys/meshing/prime/internals/launcher.py b/src/ansys/meshing/prime/internals/launcher.py index 9dd06b950a..a6c311193e 100644 --- a/src/ansys/meshing/prime/internals/launcher.py +++ b/src/ansys/meshing/prime/internals/launcher.py @@ -25,6 +25,7 @@ import os import subprocess import sys +import uuid from typing import Optional import ansys.meshing.prime.internals.config as config @@ -73,6 +74,7 @@ def launch_server_process( ip: str = defaults.ip(), port: int = defaults.port(), n_procs: Optional[int] = None, + connection_type: config.ConnectionType = None, **kw, ) -> subprocess.Popen: """Launch a server process for Ansys Prime Server. @@ -153,6 +155,9 @@ def launch_server_process( server_args.append(f'--scheduler') server_args.append(f'{scheduler}') + if os.name != 'nt' and connection_type == config.ConnectionType.GRPC: + server_args.append(f'--uds={kw.get("uds_file", "")}') + kwargs = { 'stdin': subprocess.DEVNULL, } @@ -211,6 +216,7 @@ def launch_prime( ip: str = defaults.ip(), port: int = defaults.port(), timeout: float = defaults.connection_timeout(), + connection_type: config.ConnectionType = config.ConnectionType.GRPC, n_procs: Optional[int] = None, version: Optional[str] = None, **kwargs, @@ -262,8 +268,23 @@ def launch_prime( client.container_name = container_name return client + uds_file = f'unix:/tmp/pyprimemesh-{uuid.uuid4()}.sock' + server = launch_server_process( - prime_root=prime_root, ip=ip, port=port, n_procs=n_procs, **kwargs + prime_root=prime_root, + ip=ip, + port=port, + n_procs=n_procs, + connection_type=connection_type, + uds_file=uds_file, + **kwargs, ) - return Client(server_process=server, ip=ip, port=port, timeout=timeout) + return Client( + server_process=server, + ip=ip, + port=port, + timeout=timeout, + uds_file=uds_file, + connection_type=connection_type, + )