11"""Session manager for gRPC."""
2+
23from contextlib import contextmanager
34from enum import Enum
5+ import errno
46from shutil import which
7+ import socket
58from struct import pack , unpack
69import subprocess
710from sys import modules
142145from ansys .edb .core .inner .exceptions import EDBSessionException , ErrorCode
143146from ansys .edb .core .inner .interceptors import ExceptionInterceptor , LoggingInterceptor
144147
148+ DEFAULT_ADDRESS = "localhost"
149+
145150# The session module singleton
146151MOD = modules [__name__ ]
147152MOD .current_session = None
@@ -172,8 +177,8 @@ def __init__(self, ip_address, port_num, ansys_em_root):
172177 if MOD .current_session is not None :
173178 raise EDBSessionException (ErrorCode .STARTUP_MULTI_SESSIONS )
174179
175- self .ip_address = ip_address or "localhost"
176- self .port_num = port_num
180+ self .ip_address = ip_address or DEFAULT_ADDRESS
181+ self .port_num = port_num or _find_available_port ()
177182 self .ansys_em_root = ansys_em_root
178183 self .channel = None
179184 self .local_server_proc = None
@@ -457,8 +462,8 @@ def launch_session(ansys_em_root, port_num=None):
457462 ansys_em_root : str
458463 Directory where the ``EDB_RPC_Server.exe`` file is installed.
459464 port_num : int, default: None
460- Port number to listen on. The default is ``None``, in which
461- case localhost is used .
465+ Port number to listen on. The default is ``None``, in which case a port in [50051, 60000]
466+ is selected .
462467
463468 Examples
464469 --------
@@ -480,15 +485,16 @@ def launch_session(ansys_em_root, port_num=None):
480485
481486
482487@contextmanager
483- def session (ansys_em_root , port_num , ip_address = None ):
488+ def session (ansys_em_root : str , port_num : int = None , ip_address : str = None ):
484489 r"""Launch a local session to an EDB API server in a context manager.
485490
486491 Parameters
487492 ----------
488493 ansys_em_root : str
489494 Directory where the ``EDB_RPC_Server.exe`` file is installed.
490- port_num : int
491- Port number to listen on.
495+ port_num : int, default: None
496+ Port number to listen on. The default is ``None``, in which case a port in [50051, 60000]
497+ is selected.
492498 ip_address : str, default: None
493499 IP address where the server executable file is running. The default is ``None``, in which
494500 case localhost is used.
@@ -573,3 +579,22 @@ def _ensure_session(ansys_em_root, port_num, ip_address):
573579 else :
574580 MOD .current_session = _Session (ip_address , port_num , ansys_em_root )
575581 MOD .current_session .connect ()
582+
583+
584+ def _find_available_port (interface : str = None , start_port : int = 50051 , end_port : int = 60000 ):
585+ """Find an available port in the given range.
586+
587+ Parameters
588+ ----------
589+ interface : str, default: :data:`DEFAULT_ADDRESS`
590+ Interface to check for available ports.
591+ start_port : int, default: ``50051``
592+ First port number to check.
593+ end_port : int, default: ``60000``
594+ Last port number to check.
595+ """
596+ for port in range (start_port , end_port ):
597+ with socket .socket (socket .AF_INET , socket .SOCK_STREAM ) as sock :
598+ if sock .connect_ex ((interface or DEFAULT_ADDRESS , port )) == errno .ECONNREFUSED :
599+ return port
600+ raise RuntimeError ("No available ports found" )
0 commit comments