2424import os
2525import pathlib
2626import socket
27- from typing import Any , DefaultDict , Dict , Iterator , Protocol , TextIO , Union
27+ from typing import Any , DefaultDict , Dict , Protocol , TextIO , Union
2828
2929import typer
3030import yaml
@@ -59,7 +59,7 @@ def dynamo_address(self) -> tuple[str, str]:
5959
6060
6161class PortReserver :
62- def __init__ (self , host : str ):
62+ def __init__ (self , host : str = "localhost" ):
6363 self .host = host
6464 self .socket = None
6565 self .port = None
@@ -71,10 +71,14 @@ def __enter__(self) -> int:
7171 _ , self .port = self .socket .getsockname ()
7272 return self .port
7373 except socket .error as e :
74+ self .close_socket ()
7475 logger .warning (f"Failed to reserve port on { self .host } : { str (e )} " )
7576 raise
7677
7778 def __exit__ (self , exc_type , exc_val , exc_tb ):
79+ self .close_socket ()
80+
81+ def close_socket (self ):
7882 try :
7983 if self .socket :
8084 self .socket .close ()
@@ -84,10 +88,13 @@ def __exit__(self, exc_type, exc_val, exc_tb):
8488 return True
8589
8690
87- def reserve_free_port (host : str = "localhost" ) -> Iterator [int ]:
91+ @contextlib .contextmanager
92+ def reserve_free_port (
93+ host : str = "localhost" ,
94+ ) -> contextlib .AbstractContextManager [int ]:
8895 """
8996 Detect free port and reserve until exit the context.
90- Returns an iterator that yields the reserved port.
97+ Returns a context manager that yields the reserved port.
9198 """
9299 with PortReserver (host ) as port :
93100 yield port
0 commit comments