11from dataclasses import dataclass
22import subprocess
3+ import json
4+ import time
35
46from .docker_command_failed_error import DockerCommandFailedError
57from ..util .remove_whitespace import remove_whitespace
@@ -24,10 +26,7 @@ def get_exposed_port(self, internal_port: int = 3_000) -> int:
2426 docker_command = [
2527 'docker' ,
2628 'inspect' ,
27- # this is a string that cannot be break apart in a useful way
28- # pylint: disable=line-too-long
29- f'--format=\' {{{{(index (index .NetworkSettings.Ports "{ internal_port } /tcp") 0).HostPort}}}}\' ' ,
30- # pylint: enable=line-too-long
29+ f'--format={{{{json .NetworkSettings.Ports}}}}' ,
3130 self .id
3231 ]
3332
@@ -41,14 +40,35 @@ def get_exposed_port(self, internal_port: int = 3_000) -> int:
4140 if process .returncode != 0 :
4241 raise DockerCommandFailedError (
4342 f'Inspect failed with output: { stderr .decode ("utf-8" )} ' )
43+
44+ # Parse the JSON output to get the port mapping
45+ try :
46+ port_mappings = json .loads (stdout .decode ('utf-8' ).strip ())
47+ port_key = f"{ internal_port } /tcp"
48+
49+ if port_key not in port_mappings or not port_mappings [port_key ]:
50+ # Wait briefly and try to restart/refresh the container
51+ time .sleep (1 )
52+ self .restart ()
53+ time .sleep (2 ) # Wait for container to be ready
54+
55+ # Try one more time after restart
56+ return self .get_exposed_port (internal_port )
57+
58+ return int (port_mappings [port_key ][0 ]['HostPort' ])
59+ except (json .JSONDecodeError , KeyError , IndexError , TypeError ) as e :
60+ raise DockerCommandFailedError (
61+ f'Failed to parse port mapping: { stdout .decode ("utf-8" )} . Error: { str (e )} ' )
4462
45- exposed_port_as_string = remove_whitespace (
46- stdout .decode ('utf-8' )
47- ).replace ('\' ' , '' ).replace ('|' , '' )
48-
49- try :
50- return int (exposed_port_as_string )
51- except ValueError as value_error :
52- raise ValueError (
53- f'Could not parse port from: { exposed_port_as_string } '
54- ) from value_error
63+ def restart (self ):
64+ """Restart the container"""
65+ docker_command = ['docker' , 'restart' , self .id ]
66+ with subprocess .Popen (
67+ docker_command ,
68+ stdout = subprocess .PIPE ,
69+ stderr = subprocess .PIPE
70+ ) as process :
71+ process .communicate ()
72+
73+ if process .returncode != 0 :
74+ raise DockerCommandFailedError ('Failed to restart container' )
0 commit comments