diff --git a/src/ert/ensemble_evaluator/config.py b/src/ert/ensemble_evaluator/config.py index 3f2b8ca3f62..79c127cccdb 100644 --- a/src/ert/ensemble_evaluator/config.py +++ b/src/ert/ensemble_evaluator/config.py @@ -17,8 +17,8 @@ from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.x509.oid import NameOID +from ert.shared import find_available_socket from ert.shared import get_machine_name as ert_shared_get_machine_name -from ert.shared import port_handler from .evaluator_connection_info import EvaluatorConnectionInfo @@ -128,16 +128,16 @@ def __init__( generate_cert: bool = True, custom_host: typing.Optional[str] = None, ) -> None: - self.host, self.port, self._socket_handle = port_handler.find_available_port( + self._socket_handle = find_available_socket( custom_range=custom_port_range, custom_host=custom_host ) + host, port = self._socket_handle.getsockname() self.protocol = "wss" if generate_cert else "ws" - self.url = f"{self.protocol}://{self.host}:{self.port}" + self.url = f"{self.protocol}://{host}:{port}" self.client_uri = f"{self.url}/client" self.dispatch_uri = f"{self.url}/dispatch" - if generate_cert: - cert, key, pw = _generate_certificate(ip_address=self.host) + cert, key, pw = _generate_certificate(host) else: cert, key, pw = None, None, None self.cert = cert @@ -151,8 +151,6 @@ def get_socket(self) -> socket.socket: def get_connection_info(self) -> EvaluatorConnectionInfo: return EvaluatorConnectionInfo( - self.host, - self.port, self.url, self.cert, self.token, diff --git a/src/ert/ensemble_evaluator/evaluator_connection_info.py b/src/ert/ensemble_evaluator/evaluator_connection_info.py index d788718129c..bd48e08e4a1 100644 --- a/src/ert/ensemble_evaluator/evaluator_connection_info.py +++ b/src/ert/ensemble_evaluator/evaluator_connection_info.py @@ -6,8 +6,6 @@ class EvaluatorConnectionInfo: """Read only server-info""" - host: str - port: int url: str cert: Optional[Union[str, bytes]] = None token: Optional[str] = None diff --git a/src/ert/services/_storage_main.py b/src/ert/services/_storage_main.py index 1ae50044222..487e6870e12 100644 --- a/src/ert/services/_storage_main.py +++ b/src/ert/services/_storage_main.py @@ -18,7 +18,7 @@ from ert.logging import STORAGE_LOG_CONFIG from ert.plugins import ErtPluginContext from ert.shared import __file__ as ert_shared_path -from ert.shared import port_handler +from ert.shared import find_available_socket from ert.shared.storage.command import add_parser_options @@ -95,8 +95,7 @@ def run_server(args: Optional[argparse.Namespace] = None, debug: bool = False) - config_args.update(reload=True, reload_dirs=[os.path.dirname(ert_shared_path)]) os.environ["ERT_STORAGE_DEBUG"] = "1" - _, _, sock = port_handler.find_available_port(custom_host=args.host) - + sock = find_available_socket(custom_host=args.host) connection_info = _create_connection_info(sock, authtoken) # Appropriated from uvicorn.main:run diff --git a/src/ert/shared/__init__.py b/src/ert/shared/__init__.py index 5defc6e5346..ff476fb272d 100644 --- a/src/ert/shared/__init__.py +++ b/src/ert/shared/__init__.py @@ -16,6 +16,6 @@ def ert_share_path() -> str: return str(Path(spec_origin).parent.parent / "resources") -from .port_handler import get_machine_name +from .net_utils import find_available_socket, get_machine_name -__all__ = ["__version__", "ert_share_path", "get_machine_name"] +__all__ = ["__version__", "ert_share_path", "find_available_socket", "get_machine_name"] diff --git a/src/ert/shared/port_handler.py b/src/ert/shared/net_utils.py similarity index 89% rename from src/ert/shared/port_handler.py rename to src/ert/shared/net_utils.py index 956bba5f723..66c12aef6c9 100644 --- a/src/ert/shared/port_handler.py +++ b/src/ert/shared/net_utils.py @@ -1,7 +1,7 @@ import logging import random import socket -from typing import Optional, Tuple +from typing import Optional from dns import exception, resolver, reversename @@ -46,11 +46,11 @@ def get_machine_name() -> str: return "localhost" -def find_available_port( +def find_available_socket( custom_host: Optional[str] = None, custom_range: Optional[range] = None, will_close_then_reopen_socket: bool = False, -) -> Tuple[str, int, socket.socket]: +) -> socket.socket: """ The default and recommended approach here is to return a bound socket to the caller, requiring the caller to keep the socket-object alive as long as the @@ -62,7 +62,7 @@ def find_available_port( but port is not ready to be re-bound yet, and 2) some other process managed to bind the port before the original caller gets around to re-bind. - Thus, we expect clients calling find_available_port() to keep the returned + Thus, we expect clients calling find_available_socket() to keep the returned socket-object alive and open as long as the port is needed. If a socket-object is passed to other modules like for example a websocket-server, use dup() to obtain a new Python socket-object bound to the same underlying socket (and hence @@ -84,14 +84,10 @@ def find_available_port( random.shuffle(ports) for port in ports: try: - return ( - current_host, - port, - _bind_socket( - host=current_host, - port=port, - will_close_then_reopen_socket=will_close_then_reopen_socket, - ), + return _bind_socket( + host=current_host, + port=port, + will_close_then_reopen_socket=will_close_then_reopen_socket, ) except PortAlreadyInUseException: continue @@ -108,7 +104,7 @@ def _bind_socket( # Setting flags like SO_REUSEADDR and/or SO_REUSEPORT may have # undesirable side-effects but we allow it if caller insists. Refer to - # comment on find_available_port() + # comment on find_available_socket() # # See e.g. https://stackoverflow.com/a/14388707 for an extensive # explanation of these flags, in particular the part about TIME_WAIT @@ -135,10 +131,6 @@ def _bind_socket( raise OSError(f"Unknown `OSError` while binding port {port}") from err_info -def get_family_for_localhost() -> socket.AddressFamily: - return get_family(_get_ip_address()) - - def get_family(host: str) -> socket.AddressFamily: try: socket.inet_pton(socket.AF_INET6, host) diff --git a/tests/unit_tests/ensemble_evaluator/test_ensemble_evaluator_config.py b/tests/unit_tests/ensemble_evaluator/test_ensemble_evaluator_config.py index ac6bf2952bb..0049d6e656c 100644 --- a/tests/unit_tests/ensemble_evaluator/test_ensemble_evaluator_config.py +++ b/tests/unit_tests/ensemble_evaluator/test_ensemble_evaluator_config.py @@ -1,3 +1,5 @@ +from urllib.parse import urlparse + from ert.ensemble_evaluator.config import EvaluatorServerConfig @@ -13,8 +15,9 @@ def test_load_config(unused_tcp_port): expected_client_uri = f"{expected_url}/client" expected_dispatch_uri = f"{expected_url}/dispatch" - assert serv_config.host == expected_host - assert serv_config.port == expected_port + url = urlparse(serv_config.url) + assert url.hostname == expected_host + assert url.port == expected_port assert serv_config.url == expected_url assert serv_config.client_uri == expected_client_uri assert serv_config.dispatch_uri == expected_dispatch_uri diff --git a/tests/unit_tests/ensemble_evaluator/test_monitor.py b/tests/unit_tests/ensemble_evaluator/test_monitor.py index 57c13810a6c..2314af3fbf8 100644 --- a/tests/unit_tests/ensemble_evaluator/test_monitor.py +++ b/tests/unit_tests/ensemble_evaluator/test_monitor.py @@ -1,9 +1,10 @@ import asyncio import logging from http import HTTPStatus +from urllib.parse import urlparse import pytest -import websockets +from websockets import server from websockets.exceptions import ConnectionClosedOK from _ert.events import EEUserCancel, EEUserDone, event_from_json @@ -18,8 +19,9 @@ async def process_request(path, request_headers): if path == "/healthcheck": return HTTPStatus.OK, {}, b"" - async with websockets.server.serve( - handler, ee_config.host, ee_config.port, process_request=process_request + url = urlparse(ee_config.url) + async with server.serve( + handler, url.hostname, url.port, process_request=process_request ): await set_when_done.wait() @@ -36,9 +38,7 @@ async def test_no_connection_established(make_ee_config): async def test_immediate_stop(unused_tcp_port): - ee_con_info = EvaluatorConnectionInfo( - "127.0.0.1", unused_tcp_port, f"ws://127.0.0.1:{unused_tcp_port}" - ) + ee_con_info = EvaluatorConnectionInfo(f"ws://127.0.0.1:{unused_tcp_port}") set_when_done = asyncio.Event() @@ -59,9 +59,7 @@ async def mock_ws_event_handler(websocket): async def test_unexpected_close(unused_tcp_port): - ee_con_info = EvaluatorConnectionInfo( - "127.0.0.1", unused_tcp_port, f"ws://127.0.0.1:{unused_tcp_port}" - ) + ee_con_info = EvaluatorConnectionInfo(f"ws://127.0.0.1:{unused_tcp_port}") set_when_done = asyncio.Event() socket_closed = asyncio.Event() @@ -89,9 +87,7 @@ async def test_that_monitor_track_can_exit_without_terminated_event_from_evaluat unused_tcp_port, caplog ): caplog.set_level(logging.ERROR) - ee_con_info = EvaluatorConnectionInfo( - "127.0.0.1", unused_tcp_port, f"ws://127.0.0.1:{unused_tcp_port}" - ) + ee_con_info = EvaluatorConnectionInfo(f"ws://127.0.0.1:{unused_tcp_port}") set_when_done = asyncio.Event() @@ -125,9 +121,7 @@ async def test_that_monitor_can_emit_heartbeats(unused_tcp_port): exit anytime. A heartbeat is a None event. If the heartbeat is never sent, this test function will hang and then timeout.""" - ee_con_info = EvaluatorConnectionInfo( - "127.0.0.1", unused_tcp_port, f"ws://127.0.0.1:{unused_tcp_port}" - ) + ee_con_info = EvaluatorConnectionInfo(f"ws://127.0.0.1:{unused_tcp_port}") set_when_done = asyncio.Event() websocket_server_task = asyncio.create_task( diff --git a/tests/unit_tests/services/test_storage_service.py b/tests/unit_tests/services/test_storage_service.py index fe4d122316b..3fe39833244 100644 --- a/tests/unit_tests/services/test_storage_service.py +++ b/tests/unit_tests/services/test_storage_service.py @@ -4,12 +4,12 @@ from ert.services import StorageService from ert.services._storage_main import _create_connection_info -from ert.shared import port_handler +from ert.shared import find_available_socket def test_create_connection_string(): authtoken = "very_secret_token" - _, _, sock = port_handler.find_available_port() + sock = find_available_socket() _create_connection_info(sock, authtoken) diff --git a/tests/unit_tests/shared/test_port_handler.py b/tests/unit_tests/shared/test_port_handler.py index a7034bd5cad..9081b2292a1 100644 --- a/tests/unit_tests/shared/test_port_handler.py +++ b/tests/unit_tests/shared/test_port_handler.py @@ -5,7 +5,12 @@ import pytest -from ert.shared import get_machine_name, port_handler +from ert.shared import find_available_socket, get_machine_name +from ert.shared.net_utils import ( + InvalidHostException, + NoPortsInRangeException, + get_family, +) def test_that_get_machine_name_is_predictive(mocker): @@ -44,11 +49,13 @@ def test_that_get_machine_name_is_predictive(mocker): assert get_machine_name() == expected_resolved_name -def test_find_available_port(unused_tcp_port): +def test_find_available_socket(unused_tcp_port): custom_range = range(unused_tcp_port, unused_tcp_port + 1) - host, port, sock = port_handler.find_available_port( - custom_range=custom_range, custom_host="127.0.0.1" - ) + sock = find_available_socket(custom_range=custom_range, custom_host="127.0.0.1") + ( + host, + port, + ) = sock.getsockname() assert host is not None assert port is not None assert port in custom_range @@ -56,11 +63,13 @@ def test_find_available_port(unused_tcp_port): assert sock.fileno() != -1 -def test_find_available_port_forced(unused_tcp_port): +def test_find_available_socket_forced(unused_tcp_port): custom_range = range(unused_tcp_port, unused_tcp_port) - _, port, sock = port_handler.find_available_port( - custom_range=custom_range, custom_host="127.0.0.1" - ) + sock = find_available_socket(custom_range=custom_range, custom_host="127.0.0.1") + ( + _, + port, + ) = sock.getsockname() assert port == unused_tcp_port assert sock is not None assert sock.fileno() != -1 @@ -69,8 +78,8 @@ def test_find_available_port_forced(unused_tcp_port): def test_invalid_host_name(): invalid_host = "invalid_host" - with pytest.raises(port_handler.InvalidHostException) as exc_info: - port_handler.find_available_port(custom_host=invalid_host) + with pytest.raises(InvalidHostException) as exc_info: + find_available_socket(custom_host=invalid_host) assert ( "Trying to bind socket with what looks " @@ -79,33 +88,32 @@ def test_invalid_host_name(): def test_get_family(): - family_inet6 = port_handler.get_family("::1") + family_inet6 = get_family("::1") assert family_inet6 == socket.AF_INET6 - family_inet = port_handler.get_family("host:port") + family_inet = get_family("host:port") assert family_inet == socket.AF_INET - family_inet = port_handler.get_family("host") + family_inet = get_family("host") assert family_inet == socket.AF_INET def test_gc_closes_socket(unused_tcp_port): custom_range = range(unused_tcp_port, unused_tcp_port + 1) - _, port, orig_sock = port_handler.find_available_port( + orig_sock = find_available_socket( custom_range=custom_range, custom_host="127.0.0.1" ) + _, port = orig_sock.getsockname() assert port == unused_tcp_port assert orig_sock is not None assert orig_sock.fileno() != -1 - with pytest.raises(port_handler.NoPortsInRangeException): - port_handler.find_available_port( - custom_range=custom_range, custom_host="127.0.0.1" - ) + with pytest.raises(NoPortsInRangeException): + find_available_socket(custom_range=custom_range, custom_host="127.0.0.1") - with pytest.raises(port_handler.NoPortsInRangeException): - port_handler.find_available_port( + with pytest.raises(NoPortsInRangeException): + find_available_socket( custom_range=custom_range, will_close_then_reopen_socket=True, custom_host="127.0.0.1", @@ -113,9 +121,10 @@ def test_gc_closes_socket(unused_tcp_port): orig_sock = None - _, port, orig_sock = port_handler.find_available_port( + orig_sock = find_available_socket( custom_range=custom_range, custom_host="127.0.0.1" ) + _, port = orig_sock.getsockname() assert port == unused_tcp_port assert orig_sock is not None assert orig_sock.fileno() != -1 @@ -160,7 +169,7 @@ def run(self): # - original socket live or closed # # The test-names encodes the permutation, the platform and finally -# whether subsequent calls to find_available_port() succeeds with +# whether subsequent calls to find_available_socket() succeeds with # default-mode and/or reuse-mode. For example: # # test_def_active_close_macos_nok_ok @@ -215,22 +224,21 @@ def test_def_passive_live_nok_nok_close_ok_ok(unused_tcp_port): custom_range = range(unused_tcp_port, unused_tcp_port + 1) # Opening original socket with will_close_then_reopen_socket=False - _, port, orig_sock = port_handler.find_available_port( + orig_sock = find_available_socket( custom_range=custom_range, custom_host="127.0.0.1" ) + _, port = orig_sock.getsockname() assert port == unused_tcp_port assert orig_sock is not None assert orig_sock.fileno() != -1 # When the socket is kept open, this port can not be reused # with or without setting will_close_then_reopen_socket - with pytest.raises(port_handler.NoPortsInRangeException): - port_handler.find_available_port( - custom_range=custom_range, custom_host="127.0.0.1" - ) + with pytest.raises(NoPortsInRangeException): + find_available_socket(custom_range=custom_range, custom_host="127.0.0.1") - with pytest.raises(port_handler.NoPortsInRangeException): - port_handler.find_available_port( + with pytest.raises(NoPortsInRangeException): + find_available_socket( custom_range=custom_range, custom_host="127.0.0.1", will_close_then_reopen_socket=True, @@ -240,9 +248,8 @@ def test_def_passive_live_nok_nok_close_ok_ok(unused_tcp_port): # When we close the socket without actually having used it, it is # immediately reusable with or without setting will_close_then_reopen_socket - _, port, sock = port_handler.find_available_port( - custom_range=custom_range, custom_host="127.0.0.1" - ) + sock = find_available_socket(custom_range=custom_range, custom_host="127.0.0.1") + _, port = sock.getsockname() assert port == unused_tcp_port assert sock is not None assert sock.fileno() != -1 @@ -250,11 +257,12 @@ def test_def_passive_live_nok_nok_close_ok_ok(unused_tcp_port): # we want to try again, so close it sock.close() - _, port, sock = port_handler.find_available_port( + sock = find_available_socket( custom_range=custom_range, custom_host="127.0.0.1", will_close_then_reopen_socket=True, ) + _, port = sock.getsockname() assert port == unused_tcp_port assert sock is not None assert sock.fileno() != -1 @@ -273,11 +281,12 @@ def test_reuse_active_close_nok_ok(unused_tcp_port): custom_range = range(unused_tcp_port, unused_tcp_port + 1) # Note: Setting will_close_then_reopen_socket=True on original socket - host, port, orig_sock = port_handler.find_available_port( + orig_sock = find_available_socket( custom_range=custom_range, custom_host="127.0.0.1", will_close_then_reopen_socket=True, ) + host, port = orig_sock.getsockname() assert port == unused_tcp_port assert orig_sock is not None assert orig_sock.fileno() != -1 @@ -287,17 +296,16 @@ def test_reuse_active_close_nok_ok(unused_tcp_port): orig_sock.close() # Using will_close_then_reopen_socket=False fails... - with pytest.raises(port_handler.NoPortsInRangeException): - port_handler.find_available_port( - custom_range=custom_range, custom_host="127.0.0.1" - ) + with pytest.raises(NoPortsInRangeException): + find_available_socket(custom_range=custom_range, custom_host="127.0.0.1") # ... but using will_close_then_reopen_socket=True succeeds - _, port, sock = port_handler.find_available_port( + sock = find_available_socket( custom_range=custom_range, custom_host="127.0.0.1", will_close_then_reopen_socket=True, ) + host, port = sock.getsockname() assert port == unused_tcp_port assert sock is not None assert sock.fileno() != -1 @@ -315,11 +323,12 @@ def test_reuse_active_live_nok_nok(unused_tcp_port): """ custom_range = range(unused_tcp_port, unused_tcp_port + 1) - host, port, orig_sock = port_handler.find_available_port( + orig_sock = find_available_socket( custom_range=custom_range, custom_host="127.0.0.1", will_close_then_reopen_socket=True, ) + host, port = orig_sock.getsockname() assert port == unused_tcp_port assert orig_sock is not None assert orig_sock.fileno() != -1 @@ -329,13 +338,11 @@ def test_reuse_active_live_nok_nok(unused_tcp_port): # Even with "will_close_then_reopen_socket"=True when obtaining original # socket, subsequent calls fails - with pytest.raises(port_handler.NoPortsInRangeException): - port_handler.find_available_port( - custom_range=custom_range, custom_host="127.0.0.1" - ) + with pytest.raises(NoPortsInRangeException): + find_available_socket(custom_range=custom_range, custom_host="127.0.0.1") - with pytest.raises(port_handler.NoPortsInRangeException): - _, port, _ = port_handler.find_available_port( + with pytest.raises(NoPortsInRangeException): + find_available_socket( custom_range=custom_range, custom_host="127.0.0.1", will_close_then_reopen_socket=True, @@ -353,9 +360,10 @@ def test_def_active_live_nok_nok(unused_tcp_port): """ custom_range = range(unused_tcp_port, unused_tcp_port + 1) - host, port, orig_sock = port_handler.find_available_port( + orig_sock = find_available_socket( custom_range=custom_range, custom_host="127.0.0.1" ) + host, port = orig_sock.getsockname() assert port == unused_tcp_port assert orig_sock is not None assert orig_sock.fileno() != -1 @@ -364,14 +372,12 @@ def test_def_active_live_nok_nok(unused_tcp_port): _simulate_server(host, port, orig_sock) # Immediately trying to bind to the same port fails... - with pytest.raises(port_handler.NoPortsInRangeException): - host, port, _ = port_handler.find_available_port( - custom_range=custom_range, custom_host="127.0.0.1" - ) + with pytest.raises(NoPortsInRangeException): + find_available_socket(custom_range=custom_range, custom_host="127.0.0.1") # ... also using will_close_then_reopen_socket=True - with pytest.raises(port_handler.NoPortsInRangeException): - host, port, _ = port_handler.find_available_port( + with pytest.raises(NoPortsInRangeException): + find_available_socket( custom_range=custom_range, custom_host="127.0.0.1", will_close_then_reopen_socket=True, @@ -395,9 +401,10 @@ def test_def_active_close_macos_nok_ok(unused_tcp_port): """ custom_range = range(unused_tcp_port, unused_tcp_port + 1) - host, port, orig_sock = port_handler.find_available_port( + orig_sock = find_available_socket( custom_range=custom_range, custom_host="127.0.0.1" ) + host, port = orig_sock.getsockname() assert port == unused_tcp_port assert orig_sock is not None assert orig_sock.fileno() != -1 @@ -407,18 +414,17 @@ def test_def_active_close_macos_nok_ok(unused_tcp_port): orig_sock.close() # Immediately trying to bind to the same port fails - with pytest.raises(port_handler.NoPortsInRangeException): - _, _, sock = port_handler.find_available_port( - custom_range=custom_range, custom_host="127.0.0.1" - ) + with pytest.raises(NoPortsInRangeException): + find_available_socket(custom_range=custom_range, custom_host="127.0.0.1") # On MacOS, setting will_close_then_reopen_socket=True in subsequent calls allows # to reuse the port - _, port, sock = port_handler.find_available_port( + sock = find_available_socket( custom_range=custom_range, custom_host="127.0.0.1", will_close_then_reopen_socket=True, ) + host, port = sock.getsockname() assert port == unused_tcp_port assert sock is not None assert sock.fileno() != -1 @@ -440,9 +446,10 @@ def test_def_active_close_linux_nok_nok(unused_tcp_port): """ custom_range = range(unused_tcp_port, unused_tcp_port + 1) - host, port, orig_sock = port_handler.find_available_port( + orig_sock = find_available_socket( custom_range=custom_range, custom_host="127.0.0.1" ) + host, port = orig_sock.getsockname() assert port == unused_tcp_port assert orig_sock is not None assert orig_sock.fileno() != -1 @@ -452,15 +459,13 @@ def test_def_active_close_linux_nok_nok(unused_tcp_port): orig_sock.close() # Immediately trying to bind to the same port fails - with pytest.raises(port_handler.NoPortsInRangeException): - host, port, _ = port_handler.find_available_port( - custom_range=custom_range, custom_host="127.0.0.1" - ) + with pytest.raises(NoPortsInRangeException): + find_available_socket(custom_range=custom_range, custom_host="127.0.0.1") # On Linux, setting will_close_then_reopen_socket=True in subsequent calls do # NOT allow reusing the port in this case - with pytest.raises(port_handler.NoPortsInRangeException): - host, port, _ = port_handler.find_available_port( + with pytest.raises(NoPortsInRangeException): + find_available_socket( custom_range=custom_range, custom_host="127.0.0.1", will_close_then_reopen_socket=True, @@ -481,24 +486,23 @@ def test_reuse_passive_live_macos_nok_nok(unused_tcp_port): """ custom_range = range(unused_tcp_port, unused_tcp_port + 1) - _, port, orig_sock = port_handler.find_available_port( + orig_sock = find_available_socket( custom_range=custom_range, custom_host="127.0.0.1", will_close_then_reopen_socket=True, ) + _, port = orig_sock.getsockname() assert port == unused_tcp_port assert orig_sock is not None assert orig_sock.fileno() != -1 # As long as the socket is kept alive this port can not be bound again... - with pytest.raises(port_handler.NoPortsInRangeException): - port_handler.find_available_port( - custom_range=custom_range, custom_host="127.0.0.1" - ) + with pytest.raises(NoPortsInRangeException): + find_available_socket(custom_range=custom_range, custom_host="127.0.0.1") # ... not even when setting will_close_then_reopen_socket=True - with pytest.raises(port_handler.NoPortsInRangeException): - port_handler.find_available_port( + with pytest.raises(NoPortsInRangeException): + find_available_socket( custom_range=custom_range, custom_host="127.0.0.1", will_close_then_reopen_socket=True, @@ -521,28 +525,28 @@ def test_reuse_passive_live_linux_nok_ok(unused_tcp_port): custom_range = range(unused_tcp_port, unused_tcp_port + 1) # Opening original socket with will_close_then_reopen_socket=True - _, port, orig_sock = port_handler.find_available_port( + orig_sock = find_available_socket( custom_range=custom_range, custom_host="127.0.0.1", will_close_then_reopen_socket=True, ) + _, port = orig_sock.getsockname() assert port == unused_tcp_port assert orig_sock is not None assert orig_sock.fileno() != -1 # As long as the socket is kept alive this port can not be bound again... - with pytest.raises(port_handler.NoPortsInRangeException): - port_handler.find_available_port( - custom_range=custom_range, custom_host="127.0.0.1" - ) + with pytest.raises(NoPortsInRangeException): + find_available_socket(custom_range=custom_range, custom_host="127.0.0.1") # ... but on Linux the port can be re-bound by setting this flag! # This does not seem safe in a multi-user/-process environment! - _, port, sock = port_handler.find_available_port( + sock = find_available_socket( custom_range=custom_range, custom_host="127.0.0.1", will_close_then_reopen_socket=True, ) + _, port = orig_sock.getsockname() assert port == unused_tcp_port assert sock is not None assert sock.fileno() != -1 @@ -559,11 +563,12 @@ def test_reuse_passive_close_ok_ok(unused_tcp_port): """ custom_range = range(unused_tcp_port, unused_tcp_port + 1) - _, port, orig_sock = port_handler.find_available_port( + orig_sock = find_available_socket( custom_range=custom_range, custom_host="127.0.0.1", will_close_then_reopen_socket=True, ) + _, port = orig_sock.getsockname() assert port == unused_tcp_port assert orig_sock is not None assert orig_sock.fileno() != -1 @@ -572,9 +577,8 @@ def test_reuse_passive_close_ok_ok(unused_tcp_port): # When we close the socket without actually having used it, it is # immediately reusable with or without setting will_close_then_reopen_socket - _, port, sock = port_handler.find_available_port( - custom_range=custom_range, custom_host="127.0.0.1" - ) + sock = find_available_socket(custom_range=custom_range, custom_host="127.0.0.1") + _, port = sock.getsockname() assert port == unused_tcp_port assert sock is not None assert sock.fileno() != -1 @@ -582,11 +586,12 @@ def test_reuse_passive_close_ok_ok(unused_tcp_port): # we want to try again, so close it sock.close() - _, port, sock = port_handler.find_available_port( + sock = find_available_socket( custom_range=custom_range, custom_host="127.0.0.1", will_close_then_reopen_socket=True, ) + _, port = sock.getsockname() assert port == unused_tcp_port assert sock is not None assert sock.fileno() != -1