3535"""
3636
3737import getpass
38+ import io
3839import json
3940import logging
4041import os
@@ -272,25 +273,16 @@ class OMCSessionZMQ:
272273
273274 def __init__ (self ,
274275 timeout : float = 10.00 ,
275- docker : Optional [str ] = None ,
276- dockerContainer : Optional [int ] = None ,
277- dockerExtraArgs : Optional [list ] = None ,
278- dockerOpenModelicaPath : str = "omc" ,
279- dockerNetwork : Optional [str ] = None ,
280- port : Optional [int ] = None ,
281276 omhome : Optional [str ] = None ):
282- if dockerExtraArgs is None :
283- dockerExtraArgs = []
284277
285- self ._omhome = self ._get_omhome (omhome = omhome )
278+ # store variables
279+ self ._omhome = self ._omc_home_get (omhome = omhome )
280+ self ._timeout = timeout
281+
282+ # variables to store compiled re expressions use in self.sendExpression()
283+ self ._re_log_entries = None
284+ self ._re_log_raw = None
286285
287- self ._omc_process = None
288- self ._omc_command = None
289- self ._omc : Optional [Any ] = None
290- self ._dockerCid : Optional [int ] = None
291- self ._serverIPAddress = "127.0.0.1"
292- self ._interactivePort = None
293- self ._temp_dir = pathlib .Path (tempfile .gettempdir ())
294286 # generate a random string for this session
295287 self ._random_string = uuid .uuid4 ().hex
296288 try :
@@ -301,38 +293,50 @@ def __init__(self,
301293 # We are running as a uid not existing in the password database... Pretend we are nobody
302294 self ._currentUser = "nobody"
303295
304- self ._docker = docker
305- self ._dockerContainer = dockerContainer
306- self ._dockerExtraArgs = dockerExtraArgs
307- self ._dockerOpenModelicaPath = dockerOpenModelicaPath
308- self ._dockerNetwork = dockerNetwork
309- self ._omc_log_file = self ._create_omc_log_file ("port" )
310- self ._timeout = timeout
311296 # Locating and using the IOR
312- if sys .platform != 'win32' or docker or dockerContainer :
313- port_file = "openmodelica." + self ._currentUser + ".port." + self ._random_string
297+ self ._temp_dir = pathlib .Path (tempfile .gettempdir ())
298+ self ._omc_file_port = self ._temp_dir / self ._filename_port (current_user = self ._currentUser ,
299+ random_str = self ._random_string )
300+
301+ if sys .platform == 'win32' :
302+ filename_log = f"openmodelica.port.{ self ._random_string } .log"
314303 else :
315- port_file = "openmodelica.port." + self ._random_string
316- self ._port_file = ((pathlib .Path ("/tmp" ) if docker else self ._temp_dir ) / port_file ).as_posix ()
317- self ._interactivePort = port
304+ filename_log = f"openmodelica.{ self ._currentUser } .port.{ self ._random_string } .log"
305+ self ._omc_file_log = self ._temp_dir / filename_log
306+ # this file must be closed in the destructor
307+ self ._omc_filehandle_log : Optional [io .TextIOWrapper ] = None
308+ try :
309+ self ._omc_filehandle_log = open (self ._temp_dir / filename_log , "w+" )
310+ except OSError as ex :
311+ raise OMCSessionException (f"Cannot open log file { self ._omc_file_log } ." ) from ex
312+
318313 # set omc executable path and args
319- self ._omc_command = self ._set_omc_command (omc_path_and_args_list = ["--interactive=zmq " ,
320- "--locale=C " ,
314+ self ._omc_command = self ._omc_command_get (omc_path_and_args_list = ["--locale=C " ,
315+ "--interactive=zmq " ,
321316 f"-z={ self ._random_string } " ])
322317 # start up omc executable, which is waiting for the ZMQ connection
323- self ._omc_process = self ._start_omc_process (timeout )
318+ self ._omc_process = self ._omc_process_get (timeout )
324319 # connect to the running omc instance using ZMQ
325- self ._omc_port = self ._connect_to_omc (timeout )
320+ self ._omc_port = self ._omc_port_get (timeout )
326321
327- self ._re_log_entries = None
328- self ._re_log_raw = None
322+ # Create the ZeroMQ socket and connect to OMC server
323+ context = zmq .Context .instance ()
324+ omc = context .socket (zmq .REQ )
325+ omc .setsockopt (zmq .LINGER , 0 ) # Dismisses pending messages if closed
326+ omc .setsockopt (zmq .IMMEDIATE , True ) # Queue messages only to completed connections
327+ omc .connect (self ._omc_port )
328+
329+ self ._omc = omc
329330
330331 def __del__ (self ):
331332 try :
332333 self .sendExpression ("quit()" )
333334 except OMCSessionException :
334335 pass
335- self ._omc_log_file .close ()
336+
337+ if self ._omc_filehandle_log is not None :
338+ self ._omc_filehandle_log .close ()
339+
336340 try :
337341 self ._omc_process .wait (timeout = 2.0 )
338342 except subprocess .TimeoutExpired :
@@ -342,136 +346,41 @@ def __del__(self):
342346 self ._omc_process .kill ()
343347 self ._omc_process .wait ()
344348
345- def _create_omc_log_file (self , suffix ): # output?
346- if sys .platform == 'win32' :
347- log_filename = f"openmodelica.{ suffix } .{ self ._random_string } .log"
349+ @staticmethod
350+ def _filename_port (current_user : str , random_str : str ) -> str :
351+ if sys .platform != 'win32' :
352+ filename = f"openmodelica.{ current_user } .port.{ random_str } "
348353 else :
349- log_filename = f"openmodelica.{ self ._currentUser } .{ suffix } .{ self ._random_string } .log"
350- # this file must be closed in the destructor
351- omc_log_file = open (self ._temp_dir / log_filename , "w+" )
354+ filename = f"openmodelica.port.{ random_str } "
352355
353- return omc_log_file
356+ return filename
357+
358+ def _omc_process_get (self , timeout ): # output?
359+ my_env = os .environ .copy ()
354360
355- def _start_omc_process (self , timeout ): # output?
356361 if sys .platform == 'win32' :
357362 omhome_bin = (self ._omhome / "bin" ).as_posix ()
358- my_env = os .environ .copy ()
359363 my_env ["PATH" ] = omhome_bin + os .pathsep + my_env ["PATH" ]
360- omc_process = subprocess .Popen (self ._omc_command , stdout = self ._omc_log_file ,
361- stderr = self ._omc_log_file , env = my_env )
362364 else :
363365 # set the user environment variable so omc running from wsgi has the same user as OMPython
364- my_env = os .environ .copy ()
365366 my_env ["USER" ] = self ._currentUser
366- omc_process = subprocess .Popen (self ._omc_command , stdout = self ._omc_log_file ,
367- stderr = self ._omc_log_file , env = my_env )
368- if self ._docker :
369- for i in range (0 , 40 ):
370- try :
371- with open (self ._dockerCidFile , "r" ) as fin :
372- self ._dockerCid = fin .read ().strip ()
373- except IOError :
374- pass
375- if self ._dockerCid :
376- break
377- time .sleep (timeout / 40.0 )
378- try :
379- os .remove (self ._dockerCidFile )
380- except FileNotFoundError :
381- pass
382- if self ._dockerCid is None :
383- logger .error ("Docker did not start. Log-file says:\n %s" % (open (self ._omc_log_file .name ).read ()))
384- raise OMCSessionException ("Docker did not start (timeout=%f might be too short especially if you did "
385- "not docker pull the image before this command)." % timeout )
386-
387- dockerTop = None
388- if self ._docker or self ._dockerContainer :
389- if self ._dockerNetwork == "separate" :
390- output = subprocess .check_output (["docker" , "inspect" , self ._dockerCid ]).decode ().strip ()
391- self ._serverIPAddress = json .loads (output )[0 ]["NetworkSettings" ]["IPAddress" ]
392- for i in range (0 , 40 ):
393- if sys .platform == 'win32' :
394- break
395- dockerTop = subprocess .check_output (["docker" , "top" , self ._dockerCid ]).decode ().strip ()
396- omc_process = None
397- for line in dockerTop .split ("\n " ):
398- columns = line .split ()
399- if self ._random_string in line :
400- try :
401- omc_process = DummyPopen (int (columns [1 ]))
402- except psutil .NoSuchProcess :
403- raise OMCSessionException (
404- f"Could not find PID { dockerTop } - is this a docker instance spawned "
405- f"without --pid=host?\n Log-file says:\n { open (self ._omc_log_file .name ).read ()} " )
406- break
407- if omc_process is not None :
408- break
409- time .sleep (timeout / 40.0 )
410- if omc_process is None :
411- raise OMCSessionException ("Docker top did not contain omc process %s:\n %s\n Log-file says:\n %s"
412- % (self ._random_string , dockerTop , open (self ._omc_log_file .name ).read ()))
367+
368+ omc_process = subprocess .Popen (self ._omc_command , stdout = self ._omc_filehandle_log ,
369+ stderr = self ._omc_filehandle_log , env = my_env )
413370 return omc_process
414371
415- def _getuid (self ) :
372+ def _omc_command_get (self , omc_path_and_args_list ) -> list :
416373 """
417- The uid to give to docker.
418- On Windows, volumes are mapped with all files are chmod ugo+rwx,
419- so uid does not matter as long as it is not the root user.
374+ Define the command that will be called by the subprocess module.
420375 """
421- return 1000 if sys .platform == 'win32' else os .getuid ()
422-
423- def _set_omc_command (self , omc_path_and_args_list ) -> list :
424- """Define the command that will be called by the subprocess module.
425-
426- On Windows, use the list input style of the subprocess module to
427- avoid problems resulting from spaces in the path string.
428- Linux, however, only works with the string version.
429- """
430- if (self ._docker or self ._dockerContainer ) and sys .platform == "win32" :
431- extraFlags = ["-d=zmqDangerousAcceptConnectionsFromAnywhere" ]
432- if not self ._interactivePort :
433- raise OMCSessionException ("docker on Windows requires knowing which port to connect to. For "
434- "dockerContainer=..., the container needs to have already manually exposed "
435- "this port when it was started (-p 127.0.0.1:n:n) or you get an error later." )
436- else :
437- extraFlags = []
438- if self ._docker :
439- if sys .platform == "win32" :
440- p = int (self ._interactivePort )
441- dockerNetworkStr = ["-p" , "127.0.0.1:%d:%d" % (p , p )]
442- elif self ._dockerNetwork == "host" or self ._dockerNetwork is None :
443- dockerNetworkStr = ["--network=host" ]
444- elif self ._dockerNetwork == "separate" :
445- dockerNetworkStr = []
446- extraFlags = ["-d=zmqDangerousAcceptConnectionsFromAnywhere" ]
447- else :
448- raise OMCSessionException ('dockerNetwork was set to %s, but only \" host\" or \" separate\" is allowed' )
449- self ._dockerCidFile = self ._omc_log_file .name + ".docker.cid"
450- omcCommand = (["docker" , "run" ,
451- "--cidfile" , self ._dockerCidFile ,
452- "--rm" ,
453- "--env" , "USER=%s" % self ._currentUser ,
454- "--user" , str (self ._getuid ())]
455- + self ._dockerExtraArgs
456- + dockerNetworkStr
457- + [self ._docker , self ._dockerOpenModelicaPath ])
458- elif self ._dockerContainer :
459- omcCommand = (["docker" , "exec" ,
460- "--env" , "USER=%s" % self ._currentUser ,
461- "--user" , str (self ._getuid ())]
462- + self ._dockerExtraArgs
463- + [self ._dockerContainer , self ._dockerOpenModelicaPath ])
464- self ._dockerCid = self ._dockerContainer
465- else :
466- omcCommand = [str (self ._get_omc_path ())]
467- if self ._interactivePort :
468- extraFlags = extraFlags + ["--interactivePort=%d" % int (self ._interactivePort )]
376+ omc_command_base = [str (self ._omc_path_get ())]
469377
470- omc_command = omcCommand + omc_path_and_args_list + extraFlags
378+ omc_command = omc_command_base + omc_path_and_args_list
471379
472380 return omc_command
473381
474- def _get_omhome (self , omhome : Optional [str ] = None ):
382+ @staticmethod
383+ def _omc_home_get (omhome : Optional [str ] = None ):
475384 # use the provided path
476385 if omhome is not None :
477386 return pathlib .Path (omhome )
@@ -488,51 +397,44 @@ def _get_omhome(self, omhome: Optional[str] = None):
488397
489398 raise OMCSessionException ("Cannot find OpenModelica executable, please install from openmodelica.org" )
490399
491- def _get_omc_path (self ) -> pathlib .Path :
400+ def _omc_path_get (self ) -> pathlib .Path :
492401 return self ._omhome / "bin" / "omc"
493402
494- def _connect_to_omc (self , timeout ) -> str :
495- omc_zeromq_uri = "file:///" + self ._port_file
403+ def _omc_port_get (self , timeout ) -> str :
404+ omc_zeromq_uri = "file:///" + self ._omc_file_port . as_posix ()
496405 # See if the omc server is running
497406 attempts = 0
498- port = None
499407 while True :
500- if self ._dockerCid :
501- try :
502- port = subprocess .check_output (args = ["docker" ,
503- "exec" , str (self ._dockerCid ),
504- "cat" , str (self ._port_file )],
505- stderr = subprocess .DEVNULL ).decode ().strip ()
506- break
507- except subprocess .CalledProcessError :
508- pass
509- else :
510- if os .path .isfile (self ._port_file ):
511- # Read the port file
512- with open (self ._port_file , 'r' ) as f_p :
513- port = f_p .readline ()
514- os .remove (self ._port_file )
515- break
408+ port = self ._omc_port_get_main ()
409+ if port is not None :
410+ break
516411
517412 attempts += 1
518413 if attempts == 80.0 :
519- name = self ._omc_log_file .name
520- self ._omc_log_file .close ()
521- logger .error ("OMC Server did not start. Please start it! Log-file says:\n %s" % open (name ).read ())
414+ if self ._omc_filehandle_log is not None :
415+ name = self ._omc_filehandle_log .name
416+ self ._omc_filehandle_log .close ()
417+ self ._omc_filehandle_log = None
418+ log_content = open (name ).read ()
419+ else :
420+ log_content = '(missing log file)'
522421 raise OMCSessionException (f"OMC Server did not start (timeout={ timeout } ). "
523- f"Could not open file { self ._port_file } " )
422+ f"Could not open file { self ._omc_file_port .as_posix ()} . "
423+ f"Log-file says:\n { log_content } " )
524424 time .sleep (timeout / 80.0 )
525425
526- port = port .replace ("0.0.0.0" , self ._serverIPAddress )
527426 logger .info (f"OMC Server is up and running at { omc_zeromq_uri } "
528- f"pid={ self ._omc_process .pid if self ._omc_process else '?' } cid= { self . _dockerCid } " )
427+ f"pid={ self ._omc_process .pid if self ._omc_process else '?' } " )
529428
530- # Create the ZeroMQ socket and connect to OMC server
531- context = zmq .Context .instance ()
532- self ._omc = context .socket (zmq .REQ )
533- self ._omc .setsockopt (zmq .LINGER , 0 ) # Dismisses pending messages if closed
534- self ._omc .setsockopt (zmq .IMMEDIATE , True ) # Queue messages only to completed connections
535- self ._omc .connect (port )
429+ return port
430+
431+ def _omc_port_get_main (self ) -> Optional [str ]:
432+ port = None
433+ if self ._omc_file_port .is_file ():
434+ # Read the port file
435+ with open (self ._omc_file_port , 'r' ) as f_p :
436+ port = f_p .readline ()
437+ self ._omc_file_port .unlink ()
536438
537439 return port
538440
@@ -561,9 +463,9 @@ def sendExpression(self, command, parsed=True):
561463 pass
562464 attempts += 1
563465 if attempts >= 50 :
564- self ._omc_log_file .seek (0 )
565- log = self ._omc_log_file .read ()
566- self ._omc_log_file .close ()
466+ self ._omc_filehandle_log .seek (0 )
467+ log = self ._omc_filehandle_log .read ()
468+ self ._omc_filehandle_log .close ()
567469 raise OMCSessionException (f"No connection with OMC (timeout={ self ._timeout } ). Log-file says: \n { log } " )
568470 time .sleep (self ._timeout / 50.0 )
569471 if command == "quit()" :
0 commit comments