From b9766774e4e4c425d0d6fbedbc574a308aacffc5 Mon Sep 17 00:00:00 2001 From: ekrja <108271285+ekrja@users.noreply.github.com> Date: Fri, 27 Sep 2024 08:46:29 +0200 Subject: [PATCH 1/2] The get_omdb_files() method is only supported when the osl server is running on the localhost. (#335) Co-authored-by: Markus Schubert <46968437+widerschein@users.noreply.github.com> --- src/ansys/optislang/core/osl_server.py | 7 ++++ src/ansys/optislang/core/tcp/nodes.py | 10 ++++++ src/ansys/optislang/core/tcp/osl_server.py | 3 +- src/ansys/optislang/core/utils.py | 42 ++++++++++++++++++++++ tests/tcp/test_tcp_osl_server.py | 2 ++ tests/test_utils.py | 40 +++++++++++++++++++++ 6 files changed, 103 insertions(+), 1 deletion(-) diff --git a/src/ansys/optislang/core/osl_server.py b/src/ansys/optislang/core/osl_server.py index 1a3cfa6b3..bba411ace 100644 --- a/src/ansys/optislang/core/osl_server.py +++ b/src/ansys/optislang/core/osl_server.py @@ -26,6 +26,8 @@ from abc import ABC, abstractmethod from typing import NamedTuple, Optional +from ansys.optislang.core import utils + class OslVersion(NamedTuple): """optiSLang version. @@ -55,6 +57,11 @@ def __init__(self): # pragma: no cover """``OslServer`` class is an abstract base class and cannot be instantiated.""" pass + @property + def is_remote(self) -> bool: + """Determines whether the optiSLang server does not run on localhost.""" + return self.host is not None and self.port is not None and not utils.is_localhost(self.host) + @property @abstractmethod def host(self) -> Optional[str]: # pragma no cover diff --git a/src/ansys/optislang/core/tcp/nodes.py b/src/ansys/optislang/core/tcp/nodes.py index 6eb0d8a27..ec096d868 100644 --- a/src/ansys/optislang/core/tcp/nodes.py +++ b/src/ansys/optislang/core/tcp/nodes.py @@ -2140,6 +2140,9 @@ def get_inner_output_slots( def get_omdb_files(self) -> Tuple[File]: """Get paths to omdb files. + This method is supported only when the client runs on the same file + system as the server, i.e., the server is not remote. + Returns ------- Tuple[File] @@ -2153,7 +2156,14 @@ def get_omdb_files(self) -> Tuple[File]: Raised when a command or query fails. TimeoutError Raised when the timeout float value expires. + RuntimeError + Raised when the server is remote. """ + if self._osl_server.is_remote: + raise RuntimeError( + "Paths to omdb files cannot be provided when connected to the remote server." + ) + statuses_info = self._get_status_info() wdirs = [Path(status_info["working dir"]) for status_info in statuses_info] omdb_files = [] diff --git a/src/ansys/optislang/core/tcp/osl_server.py b/src/ansys/optislang/core/tcp/osl_server.py index 20ccb41a3..de0bc1fca 100644 --- a/src/ansys/optislang/core/tcp/osl_server.py +++ b/src/ansys/optislang/core/tcp/osl_server.py @@ -42,6 +42,7 @@ from deprecated.sphinx import deprecated +from ansys.optislang.core import utils from ansys.optislang.core.encoding import force_bytes, force_text from ansys.optislang.core.errors import ( ConnectionEstablishedError, @@ -799,7 +800,7 @@ def timeout(self, timeout) -> None: @property def host_addresses(self) -> List[str]: """Local IP addresses associated with self.__listener_socket.""" - addresses = [i[4][0] for i in socket.getaddrinfo(socket.gethostname(), None)] + addresses = utils.get_localhost_addresses() # Explicitly add localhost to workaround potential networking issues addresses.append("127.0.0.1") return addresses diff --git a/src/ansys/optislang/core/utils.py b/src/ansys/optislang/core/utils.py index bb76c661e..e5da2c580 100644 --- a/src/ansys/optislang/core/utils.py +++ b/src/ansys/optislang/core/utils.py @@ -25,9 +25,11 @@ import collections from enum import Enum +import ipaddress import os from pathlib import Path import re +import socket import sys from typing import ( DefaultDict, @@ -454,3 +456,43 @@ def iter_awp_roots() -> Iterator[Tuple[int, Path]]: def is_iron_python(): """Whether current platform is IronPython.""" return sys.platform == "cli" + + +def get_localhost_addresses() -> List[str]: + """Get addresses of the localhost machine. + + Returns + ------- + List[str] + List of addresses of the localhost machine. + """ + return [i[4][0] for i in socket.getaddrinfo(socket.gethostname(), None)] + + +def is_localhost(host: str) -> bool: + """Determine whether the host is localhost. + + Parameters + ---------- + host : str + IPv4/v6 address or domain name of the host. + + Returns + ------- + bool + ``True`` if the provided ``host`` is localhost; ``False`` otherwise. + """ + try: + addrinfo = socket.getaddrinfo(host=host, port=None) + except socket.gaierror: + return False + + localhost_addresses = get_localhost_addresses() + for _, _, _, _, sockaddr in addrinfo: + try: + ip = sockaddr[0] + if not ipaddress.ip_address(ip).is_loopback and ip not in localhost_addresses: + return False + except ValueError: + return False + return True diff --git a/tests/tcp/test_tcp_osl_server.py b/tests/tcp/test_tcp_osl_server.py index 5c75290a7..851b4e82d 100644 --- a/tests/tcp/test_tcp_osl_server.py +++ b/tests/tcp/test_tcp_osl_server.py @@ -335,6 +335,8 @@ def test_tcp_osl_properties(osl_server_process: OslServerProcess): tcp_osl_server.timeout = 20 assert tcp_osl_server.timeout == 20 + assert not tcp_osl_server.is_remote + with pytest.raises(ValueError): tcp_osl_server.timeout = -5 with pytest.raises(ValueError): diff --git a/tests/test_utils.py b/tests/test_utils.py index 18129dc15..936016eeb 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -21,12 +21,28 @@ # SOFTWARE. from enum import Enum +from socket import getaddrinfo, gethostname import pytest from ansys.optislang.core import utils +@pytest.fixture +def localhost_addresses(): + """Get addresses of the local machine excluding loopback addresses.""" + addresses = [] + for _, _, _, _, sockaddr in getaddrinfo(gethostname(), None): + addresses.append(sockaddr[0]) + return addresses + + +@pytest.fixture +def loopback_addresses(): + """Get loopback addresses.""" + return ["127.0.0.0", "127.0.0.1", "127.255.255.255", "::1", "localhost"] + + def test_enum_from_str(): class MyEnum(Enum): ONE = 0 @@ -42,3 +58,27 @@ class MyEnum(Enum): assert utils.enum_from_str("one", MyEnum) == MyEnum.ONE assert utils.enum_from_str("ONE", MyEnum) == MyEnum.ONE assert utils.enum_from_str("ONX", MyEnum, ["X", "E"]) == MyEnum.ONE + + +def test_get_localhost_addresses(localhost_addresses): + for address in utils.get_localhost_addresses(): + assert address in localhost_addresses + + +def test_is_localhost(loopback_addresses, localhost_addresses): + random_addresses = [ + "192.168.101.1", + "10.0.10.1", + "130.10.20.1", + "2001:0db8:85a3:0000:0000:8a2e:0370:7334", + "fe80::1ff:fe23:4567:890a%3", + ] + + for loopback_address in loopback_addresses: + assert utils.is_localhost(loopback_address) == True + + for localhost_address in localhost_addresses: + assert utils.is_localhost(localhost_address) == True + + for random_address in random_addresses: + assert utils.is_localhost(random_address) == (random_address in localhost_address) From 79c4d12bc675cba82f26c12c2339e8b404b67fa1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 29 Sep 2024 13:30:03 +0200 Subject: [PATCH 2/2] Bump ansys-sphinx-theme from 1.0.8 to 1.0.11 (#329) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Markus Schubert <46968437+widerschein@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 081181879..99af4418f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,7 +42,7 @@ tests = [ "matplotlib>=3.5.3", ] doc = [ - "ansys-sphinx-theme==1.0.8", + "ansys-sphinx-theme==1.0.11", "matplotlib==3.9.2", "numpydoc==1.7.0", "pypandoc==1.13",