Skip to content

Commit ba4e3f8

Browse files
committed
(D005) update classes in OMCSession
[OMCSessionPort] fix timeout handling [OMCSessionDocker*] improve data handling * move more code to OMCSessionDockerHelper * use _docker_omc_start() to differentiate classes * define cmd_prefix [OMCSessionWSL] define cmd_prefix [OMCSessionWSL] layout fix
1 parent 91c4f61 commit ba4e3f8

File tree

1 file changed

+110
-61
lines changed

1 file changed

+110
-61
lines changed

OMPython/OMCSession.py

Lines changed: 110 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1432,8 +1432,9 @@ class OMCSessionPort(OMCSessionABC):
14321432
def __init__(
14331433
self,
14341434
omc_port: str,
1435+
timeout: float = 10.0,
14351436
) -> None:
1436-
super().__init__()
1437+
super().__init__(timeout=timeout)
14371438
self._omc_port = omc_port
14381439

14391440

@@ -1596,7 +1597,9 @@ class OMCSessionDockerABC(OMCSessionABC, metaclass=abc.ABCMeta):
15961597

15971598
def __init__(
15981599
self,
1599-
timeout: float = 10.00,
1600+
timeout: float = 10.0,
1601+
docker: Optional[str] = None,
1602+
dockerContainer: Optional[str] = None,
16001603
dockerExtraArgs: Optional[list] = None,
16011604
dockerOpenModelicaPath: str | os.PathLike = "omc",
16021605
dockerNetwork: Optional[str] = None,
@@ -1610,11 +1613,21 @@ def __init__(
16101613
self._docker_extra_args = dockerExtraArgs
16111614
self._docker_open_modelica_path = pathlib.PurePosixPath(dockerOpenModelicaPath)
16121615
self._docker_network = dockerNetwork
1616+
self._docker_container_id: str
1617+
self._docker_process: Optional[DockerPopen]
16131618

1614-
self._interactive_port = port
1619+
# start up omc executable in docker container waiting for the ZMQ connection
1620+
self._omc_process, self._docker_process, self._docker_container_id = self._docker_omc_start(
1621+
docker_image=docker,
1622+
docker_cid=dockerContainer,
1623+
omc_port=port,
1624+
)
1625+
# connect to the running omc instance using ZMQ
1626+
self._omc_port = self._omc_port_get(docker_cid=self._docker_container_id)
1627+
if port is not None and not self._omc_port.endswith(f":{port}"):
1628+
raise OMCSessionException(f"Port mismatch: {self._omc_port} is not using the defined port {port}!")
16151629

1616-
self._docker_container_id: Optional[str] = None
1617-
self._docker_process: Optional[DockerPopen] = None
1630+
self._cmd_prefix = self.model_execution_prefix()
16181631

16191632
def _docker_process_get(self, docker_cid: str) -> Optional[DockerPopen]:
16201633
if sys.platform == 'win32':
@@ -1640,6 +1653,15 @@ def _docker_process_get(self, docker_cid: str) -> Optional[DockerPopen]:
16401653

16411654
return docker_process
16421655

1656+
@abc.abstractmethod
1657+
def _docker_omc_start(
1658+
self,
1659+
docker_image: Optional[str] = None,
1660+
docker_cid: Optional[str] = None,
1661+
omc_port: Optional[int] = None,
1662+
) -> Tuple[subprocess.Popen, DockerPopen, str]:
1663+
pass
1664+
16431665
@staticmethod
16441666
def _getuid() -> int:
16451667
"""
@@ -1651,11 +1673,14 @@ def _getuid() -> int:
16511673
# Windows, hence the type: ignore comment.
16521674
return 1000 if sys.platform == 'win32' else os.getuid() # type: ignore
16531675

1654-
def _omc_port_get(self) -> str:
1676+
def _omc_port_get(
1677+
self,
1678+
docker_cid: str,
1679+
) -> str:
16551680
port = None
16561681

1657-
if not isinstance(self._docker_container_id, str):
1658-
raise OMCSessionException(f"Invalid docker container ID: {self._docker_container_id}")
1682+
if not isinstance(docker_cid, str):
1683+
raise OMCSessionException(f"Invalid docker container ID: {docker_cid}")
16591684

16601685
# See if the omc server is running
16611686
loop = self._timeout_loop(timestep=0.1)
@@ -1664,7 +1689,7 @@ def _omc_port_get(self) -> str:
16641689
if omc_portfile_path is not None:
16651690
try:
16661691
output = subprocess.check_output(args=["docker",
1667-
"exec", self._docker_container_id,
1692+
"exec", docker_cid,
16681693
"cat", omc_portfile_path.as_posix()],
16691694
stderr=subprocess.DEVNULL)
16701695
port = output.decode().strip()
@@ -1687,7 +1712,10 @@ def get_server_address(self) -> Optional[str]:
16871712
"""
16881713
if self._docker_network == "separate" and isinstance(self._docker_container_id, str):
16891714
output = subprocess.check_output(["docker", "inspect", self._docker_container_id]).decode().strip()
1690-
return json.loads(output)[0]["NetworkSettings"]["IPAddress"]
1715+
address = json.loads(output)[0]["NetworkSettings"]["IPAddress"]
1716+
if not isinstance(address, str):
1717+
raise OMCSessionException(f"Invalid docker server address: {address}!")
1718+
return address
16911719

16921720
return None
16931721

@@ -1734,27 +1762,16 @@ def __init__(
17341762

17351763
super().__init__(
17361764
timeout=timeout,
1765+
docker=docker,
17371766
dockerExtraArgs=dockerExtraArgs,
17381767
dockerOpenModelicaPath=dockerOpenModelicaPath,
17391768
dockerNetwork=dockerNetwork,
17401769
port=port,
17411770
)
17421771

1743-
if docker is None:
1744-
raise OMCSessionException("Argument docker must be set!")
1745-
1746-
self._docker = docker
1747-
1748-
# start up omc executable in docker container waiting for the ZMQ connection
1749-
self._omc_process, self._docker_process, self._docker_container_id = self._docker_omc_start()
1750-
# connect to the running omc instance using ZMQ
1751-
self._omc_port = self._omc_port_get()
1752-
17531772
def __del__(self) -> None:
17541773

1755-
super().__del__()
1756-
1757-
if isinstance(self._docker_process, DockerPopen):
1774+
if hasattr(self, '_docker_process') and isinstance(self._docker_process, DockerPopen):
17581775
try:
17591776
self._docker_process.wait(timeout=2.0)
17601777
except subprocess.TimeoutExpired:
@@ -1766,29 +1783,37 @@ def __del__(self) -> None:
17661783
finally:
17671784
self._docker_process = None
17681785

1786+
super().__del__()
1787+
17691788
def _docker_omc_cmd(
17701789
self,
1771-
omc_path_and_args_list: list[str],
1790+
docker_image: str,
17721791
docker_cid_file: pathlib.Path,
1792+
omc_path_and_args_list: list[str],
1793+
omc_port: Optional[int | str] = None,
17731794
) -> list:
17741795
"""
17751796
Define the command that will be called by the subprocess module.
17761797
"""
1798+
17771799
extra_flags = []
17781800

17791801
if sys.platform == "win32":
17801802
extra_flags = ["-d=zmqDangerousAcceptConnectionsFromAnywhere"]
1781-
if not self._interactive_port:
1782-
raise OMCSessionException("docker on Windows requires knowing which port to connect to - "
1803+
if not self._omc_port:
1804+
raise OMCSessionException("Docker on Windows requires knowing which port to connect to - "
17831805
"please set the interactivePort argument")
17841806

1807+
port: Optional[int] = None
1808+
if isinstance(omc_port, str):
1809+
port = int(omc_port)
1810+
elif isinstance(omc_port, int):
1811+
port = omc_port
1812+
17851813
if sys.platform == "win32":
1786-
if isinstance(self._interactive_port, str):
1787-
port = int(self._interactive_port)
1788-
elif isinstance(self._interactive_port, int):
1789-
port = self._interactive_port
1790-
else:
1791-
raise OMCSessionException("Missing or invalid interactive port!")
1814+
if not isinstance(port, int):
1815+
raise OMCSessionException("OMC on Windows needs the interactive port - "
1816+
f"missing or invalid value: {repr(omc_port)}!")
17921817
docker_network_str = ["-p", f"127.0.0.1:{port}:{port}"]
17931818
elif self._docker_network == "host" or self._docker_network is None:
17941819
docker_network_str = ["--network=host"]
@@ -1799,8 +1824,8 @@ def _docker_omc_cmd(
17991824
raise OMCSessionException(f'dockerNetwork was set to {self._docker_network}, '
18001825
'but only \"host\" or \"separate\" is allowed')
18011826

1802-
if isinstance(self._interactive_port, int):
1803-
extra_flags = extra_flags + [f"--interactivePort={int(self._interactive_port)}"]
1827+
if isinstance(port, int):
1828+
extra_flags = extra_flags + [f"--interactivePort={port}"]
18041829

18051830
omc_command = ([
18061831
"docker", "run",
@@ -1810,22 +1835,33 @@ def _docker_omc_cmd(
18101835
]
18111836
+ self._docker_extra_args
18121837
+ docker_network_str
1813-
+ [self._docker, self._docker_open_modelica_path.as_posix()]
1838+
+ [docker_image, self._docker_open_modelica_path.as_posix()]
18141839
+ omc_path_and_args_list
18151840
+ extra_flags)
18161841

18171842
return omc_command
18181843

1819-
def _docker_omc_start(self) -> Tuple[subprocess.Popen, DockerPopen, str]:
1844+
def _docker_omc_start(
1845+
self,
1846+
docker_image: Optional[str] = None,
1847+
docker_cid: Optional[str] = None,
1848+
omc_port: Optional[int] = None,
1849+
) -> Tuple[subprocess.Popen, DockerPopen, str]:
1850+
1851+
if not isinstance(docker_image, str):
1852+
raise OMCSessionException("A docker image name must be provided!")
1853+
18201854
my_env = os.environ.copy()
18211855

18221856
docker_cid_file = self._temp_dir / (self._omc_filebase + ".docker.cid")
18231857

18241858
omc_command = self._docker_omc_cmd(
1859+
docker_image=docker_image,
1860+
docker_cid_file=docker_cid_file,
18251861
omc_path_and_args_list=["--locale=C",
18261862
"--interactive=zmq",
18271863
f"-z={self._random_string}"],
1828-
docker_cid_file=docker_cid_file,
1864+
omc_port=omc_port,
18291865
)
18301866

18311867
omc_process = subprocess.Popen(omc_command,
@@ -1836,6 +1872,7 @@ def _docker_omc_start(self) -> Tuple[subprocess.Popen, DockerPopen, str]:
18361872
if not isinstance(docker_cid_file, pathlib.Path):
18371873
raise OMCSessionException(f"Invalid content for docker container ID file path: {docker_cid_file}")
18381874

1875+
# the provided value for docker_cid is not used
18391876
docker_cid = None
18401877
loop = self._timeout_loop(timestep=0.1)
18411878
while next(loop):
@@ -1846,10 +1883,12 @@ def _docker_omc_start(self) -> Tuple[subprocess.Popen, DockerPopen, str]:
18461883
pass
18471884
if docker_cid is not None:
18481885
break
1849-
else:
1850-
logger.error(f"Docker did not start. Log-file says:\n{self.get_log()}")
1886+
time.sleep(self._timeout / 40.0)
1887+
1888+
if docker_cid is None:
18511889
raise OMCSessionException(f"Docker did not start (timeout={self._timeout} might be too short "
1852-
"especially if you did not docker pull the image before this command).")
1890+
"especially if you did not docker pull the image before this command). "
1891+
f"Log-file says:\n{self.get_log()}")
18531892

18541893
docker_process = self._docker_process_get(docker_cid=docker_cid)
18551894
if docker_process is None:
@@ -1876,64 +1915,71 @@ def __init__(
18761915

18771916
super().__init__(
18781917
timeout=timeout,
1918+
dockerContainer=dockerContainer,
18791919
dockerExtraArgs=dockerExtraArgs,
18801920
dockerOpenModelicaPath=dockerOpenModelicaPath,
18811921
dockerNetwork=dockerNetwork,
18821922
port=port,
18831923
)
18841924

1885-
if not isinstance(dockerContainer, str):
1886-
raise OMCSessionException("Argument dockerContainer must be set!")
1887-
1888-
self._docker_container_id = dockerContainer
1889-
1890-
# start up omc executable in docker container waiting for the ZMQ connection
1891-
self._omc_process, self._docker_process = self._docker_omc_start()
1892-
# connect to the running omc instance using ZMQ
1893-
self._omc_port = self._omc_port_get()
1894-
18951925
def __del__(self) -> None:
18961926

18971927
super().__del__()
18981928

18991929
# docker container ID was provided - do NOT kill the docker process!
19001930
self._docker_process = None
19011931

1902-
def _docker_omc_cmd(self, omc_path_and_args_list) -> list:
1932+
def _docker_omc_cmd(
1933+
self,
1934+
docker_cid: str,
1935+
omc_path_and_args_list: list[str],
1936+
omc_port: Optional[int] = None,
1937+
) -> list:
19031938
"""
19041939
Define the command that will be called by the subprocess module.
19051940
"""
19061941
extra_flags: list[str] = []
19071942

19081943
if sys.platform == "win32":
19091944
extra_flags = ["-d=zmqDangerousAcceptConnectionsFromAnywhere"]
1910-
if not self._interactive_port:
1945+
if not isinstance(omc_port, int):
19111946
raise OMCSessionException("Docker on Windows requires knowing which port to connect to - "
19121947
"Please set the interactivePort argument. Furthermore, the container needs "
19131948
"to have already manually exposed this port when it was started "
19141949
"(-p 127.0.0.1:n:n) or you get an error later.")
19151950

1916-
if isinstance(self._interactive_port, int):
1917-
extra_flags = extra_flags + [f"--interactivePort={int(self._interactive_port)}"]
1951+
if isinstance(omc_port, int):
1952+
extra_flags = extra_flags + [f"--interactivePort={omc_port}"]
19181953

19191954
omc_command = ([
19201955
"docker", "exec",
19211956
"--user", str(self._getuid()),
19221957
]
19231958
+ self._docker_extra_args
1924-
+ [self._docker_container_id, self._docker_open_modelica_path.as_posix()]
1959+
+ [docker_cid, self._docker_open_modelica_path.as_posix()]
19251960
+ omc_path_and_args_list
19261961
+ extra_flags)
19271962

19281963
return omc_command
19291964

1930-
def _docker_omc_start(self) -> Tuple[subprocess.Popen, DockerPopen]:
1965+
def _docker_omc_start(
1966+
self,
1967+
docker_image: Optional[str] = None,
1968+
docker_cid: Optional[str] = None,
1969+
omc_port: Optional[int] = None,
1970+
) -> Tuple[subprocess.Popen, DockerPopen, str]:
1971+
1972+
if not isinstance(docker_cid, str):
1973+
raise OMCSessionException("A docker container ID must be provided!")
1974+
19311975
my_env = os.environ.copy()
19321976

19331977
omc_command = self._docker_omc_cmd(
1978+
docker_cid=docker_cid,
19341979
omc_path_and_args_list=["--locale=C",
19351980
"--interactive=zmq",
19361981
f"-z={self._random_string}"],
1982+
omc_port=omc_port,
19371983
)
19381984

19391985
omc_process = subprocess.Popen(omc_command,
@@ -1942,14 +1988,14 @@ def _docker_omc_start(self) -> Tuple[subprocess.Popen, DockerPopen]:
19421988
env=my_env)
19431989

19441990
docker_process = None
1945-
if isinstance(self._docker_container_id, str):
1946-
docker_process = self._docker_process_get(docker_cid=self._docker_container_id)
1991+
if isinstance(docker_cid, str):
1992+
docker_process = self._docker_process_get(docker_cid=docker_cid)
19471993

19481994
if docker_process is None:
19491995
raise OMCSessionException(f"Docker top did not contain omc process {self._random_string} "
1950-
f"/ {self._docker_container_id}. Log-file says:\n{self.get_log()}")
1996+
f"/ {docker_cid}. Log-file says:\n{self.get_log()}")
19511997

1952-
return omc_process, docker_process
1998+
return omc_process, docker_process, docker_cid
19531999

19542000

19552001
class OMCSessionWSL(OMCSessionABC):
@@ -1977,6 +2023,8 @@ def __init__(
19772023
# connect to the running omc instance using ZMQ
19782024
self._omc_port = self._omc_port_get()
19792025

2026+
self._cmd_prefix = self.model_execution_prefix()
2027+
19802028
def model_execution_prefix(self, cwd: Optional[OMPathABC] = None) -> list[str]:
19812029
"""
19822030
Helper function which returns a command prefix needed for docker and WSL. It defaults to an empty list.
@@ -2000,7 +2048,8 @@ def _omc_process_get(self) -> subprocess.Popen:
20002048
self._wsl_omc,
20012049
"--locale=C",
20022050
"--interactive=zmq",
2003-
f"-z={self._random_string}"]
2051+
f"-z={self._random_string}",
2052+
]
20042053

20052054
omc_process = subprocess.Popen(omc_command,
20062055
stdout=self._omc_loghandle,

0 commit comments

Comments
 (0)