From c525f7db9a62bc0af6ee18abc8eb157cf779ed9d Mon Sep 17 00:00:00 2001 From: aguerre Date: Mon, 26 Aug 2013 16:11:13 +0000 Subject: [PATCH] Management of scripts+args in SALOME shell and TUI (not yet in GUI) --- bin/CMakeLists.txt | 1 + bin/appliskel/CMakeLists.txt | 2 + bin/appliskel/salome | 82 ++++------- bin/appliskel/tests/CMakeLists.txt | 20 +++ bin/appliskel/tests/launcher/CMakeLists.txt | 28 ++++ .../tests/launcher/TestLauncherSessionArgs.py | 138 ++++++++++++++++++ bin/appliskel/tests/launcher/add.py | 18 +++ bin/appliskel/tests/launcher/getLogger.py | 24 +++ bin/appliskel/tests/launcher/hello.py | 16 ++ bin/appliskel/tests/launcher/lines.py | 29 ++++ bin/launchConfigureParser.py | 48 ++---- bin/parseConfigFile.py | 4 +- bin/runConsole.py | 12 +- bin/runSalome.py | 44 ++---- bin/runSession.py | 8 +- bin/salomeConsole.py | 89 ++++++----- bin/salomeLauncherUtils.py | 112 ++++++++++++++ bin/salomeRunner.py | 68 ++++----- 18 files changed, 527 insertions(+), 216 deletions(-) create mode 100644 bin/appliskel/tests/CMakeLists.txt create mode 100644 bin/appliskel/tests/launcher/CMakeLists.txt create mode 100644 bin/appliskel/tests/launcher/TestLauncherSessionArgs.py create mode 100755 bin/appliskel/tests/launcher/add.py create mode 100644 bin/appliskel/tests/launcher/getLogger.py create mode 100755 bin/appliskel/tests/launcher/hello.py create mode 100755 bin/appliskel/tests/launcher/lines.py create mode 100644 bin/salomeLauncherUtils.py diff --git a/bin/CMakeLists.txt b/bin/CMakeLists.txt index 3be32fdee..a562f96f8 100755 --- a/bin/CMakeLists.txt +++ b/bin/CMakeLists.txt @@ -48,6 +48,7 @@ SET(SCRIPTS runSession.py runConsole.py salomeConsole.py + salomeLauncherUtils.py salomeRunner.py salome_session.py salome_utils.py diff --git a/bin/appliskel/CMakeLists.txt b/bin/appliskel/CMakeLists.txt index 9feeb7934..1e563744f 100755 --- a/bin/appliskel/CMakeLists.txt +++ b/bin/appliskel/CMakeLists.txt @@ -17,6 +17,8 @@ # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # +ADD_SUBDIRECTORY(tests) + # =============================================================== # Files to be installed # =============================================================== diff --git a/bin/appliskel/salome b/bin/appliskel/salome index d31f6c1f1..dd2342985 100755 --- a/bin/appliskel/salome +++ b/bin/appliskel/salome @@ -2,7 +2,6 @@ import os import sys -import glob # Preliminary work to initialize path to SALOME Python modules def __initialize(): @@ -28,70 +27,26 @@ def __initialize(): sys.path[:0] = [absoluteAppliPath+'/bin/salome'] # End of preliminary work -def __listDirectory(path): - allFiles = [] - for root, dirs, files in os.walk(path): - configFileNames = glob.glob(os.path.join(root,'*.cfg')) + glob.glob(os.path.join(root,'*.sh')) - allFiles += configFileNames - return allFiles -# - -def __getConfigFileNamesDefault(): - absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','') - envdDir = absoluteAppliPath + '/env.d' - if os.path.isdir(envdDir): - configFileNames = __listDirectory(envdDir) - else: - configFileNames = [] - - return configFileNames -# - -def __getConfigFileNames(args): - # special case: configuration files are provided by user - # Search for command-line argument(s) --config=file1,file2,..., filen - # Search for command-line argument(s) --config=dir1,dir2,..., dirn - configOptionPrefix = "--config=" - configArgs = [ str(x) for x in args if str(x).startswith(configOptionPrefix) ] - - if len(configArgs) == 0: - return __getConfigFileNamesDefault(), args - - args = [ x for x in args if not x.startswith(configOptionPrefix) ] - allLists = [ x.replace(configOptionPrefix, '') for x in configArgs ] - - configFileNames = [] - for currentList in allLists: - elements = currentList.split(',') - for elt in elements: - elt = os.path.realpath(os.path.expanduser(elt)) - if os.path.isdir(elt): - configFileNames += __listDirectory(elt) - else: - configFileNames += [elt] - - return configFileNames, args -# - - -if __name__ == "__main__": - args = sys.argv[1:] - +def main(args): # Identify application path then locate configuration files __initialize() - configFileNames, args = __getConfigFileNames(args) - + from salomeLauncherUtils import getConfigFileNames + configFileNames, args = getConfigFileNames(args) + + # WHY? Incorrect/Inexisting files are supposed to be ignored by SalomeRunner. + # Might simply need bug fix; please provide test case. error=False for aFile in configFileNames: if not os.path.isfile(aFile): print "ERROR: inexisting file: "+aFile error=True - if error: + if error: sys.exit(1) - + + # Create a SalomeRunner which parses configFileNames to initialize environment - from salomeRunner import SalomeRunner, SalomeRunnerException try: + from salomeRunner import SalomeRunner, SalomeRunnerException runner = SalomeRunner(configFileNames) # Here set specific variables, if needed @@ -100,6 +55,18 @@ if __name__ == "__main__": # runner.addToPythonPath('mypythonpath') # runner.setEnviron('myvarname', 'value') + kernel_root_dir = os.getenv("KERNEL_ROOT_DIR") + if kernel_root_dir: + runner.addToLdLibraryPath(os.path.join(kernel_root_dir, "lib/salome")) + + gui_root_dir = os.getenv("GUI_ROOT_DIR") + if gui_root_dir: + runner.addToLdLibraryPath(os.path.join(gui_root_dir, "lib/salome")) + + paravis_root_dir = os.getenv("PARAVIS_ROOT_DIR") + if paravis_root_dir: + runner.addToLdLibraryPath(os.path.join(paravis_root_dir, "lib/salome")) + # Start SALOME, parsing command line arguments runner.go(args) @@ -110,3 +77,8 @@ if __name__ == "__main__": logging.getLogger("salome").error(e) sys.exit(1) # + +if __name__ == "__main__": + args = sys.argv[1:] + main(args) +# diff --git a/bin/appliskel/tests/CMakeLists.txt b/bin/appliskel/tests/CMakeLists.txt new file mode 100644 index 000000000..e051906ee --- /dev/null +++ b/bin/appliskel/tests/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (C) 2013 CEA/DEN, EDF R&D, OPEN CASCADE +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +ADD_SUBDIRECTORY(launcher) diff --git a/bin/appliskel/tests/launcher/CMakeLists.txt b/bin/appliskel/tests/launcher/CMakeLists.txt new file mode 100644 index 000000000..8158f4f4f --- /dev/null +++ b/bin/appliskel/tests/launcher/CMakeLists.txt @@ -0,0 +1,28 @@ +# Copyright (C) 2013 CEA/DEN, EDF R&D, OPEN CASCADE +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +SET(SCRIPTS + add.py + getLogger.py + hello.py + lines.py + TestLauncherSessionArgs.py +) + +SALOME_INSTALL_SCRIPTS("${SCRIPTS}" ${SALOME_INSTALL_SCRIPT_SCRIPTS}/appliskel/tests/launcher) diff --git a/bin/appliskel/tests/launcher/TestLauncherSessionArgs.py b/bin/appliskel/tests/launcher/TestLauncherSessionArgs.py new file mode 100644 index 000000000..d10008603 --- /dev/null +++ b/bin/appliskel/tests/launcher/TestLauncherSessionArgs.py @@ -0,0 +1,138 @@ +import unittest + +import os +import sys +import imp +from cStringIO import StringIO +import logging + +logger = logging.getLogger("TestLauncherLogger") +logger.level = logging.DEBUG +logger.addHandler(logging.StreamHandler()) + +class TestSessionArgs(unittest.TestCase): + @classmethod + def setUpClass(cls): + # Initialize path to SALOME application + path_to_launcher = os.getenv("SALOME_LAUNCHER") + if not path_to_launcher: + msg = "Error: please set SALOME_LAUNCHER variable to the salome command of your application folder." + self.fail(msg) + # + appli_dir = os.path.dirname(path_to_launcher) + envd_dir = os.path.join(appli_dir, "env.d") + + # Configure session startup + cls.SALOME = imp.load_source("SALOME", os.path.join(appli_dir,"salome")) + cls.SALOME_args = ["shell", "--config="+envd_dir] + + cls.logFile = "log.txt" + sys.stdout = StringIO() + + # Set some predefined command args and corresponding output messages + cls.hello0 = ["hello.py", "args:outfile="+cls.logFile] + cls.hello0Msg = "Hello!" + cls.hello1 = ["hello.py", "args:you,outfile="+cls.logFile] + cls.hello1Msg = "Hello to: you" + cls.helloToAdd = ["hello.py", "args:add.py,1,2,3,outfile="+cls.logFile] + cls.helloToAddMsg = "Hello to: add.py, 1, 2, 3" + cls.add0 = ["add.py", "args:outfile="+cls.logFile] + cls.add0Msg = "No args!" + cls.add3 = ["add.py", "args:1,2,3,outfile="+cls.logFile] + cls.add3Msg = "1+2+3 = 6" + cls.lines0 = ["lines.py", "args:outfile="+cls.logFile] + cls.lines0Msg = "No files given" + cls.lines2 = ["lines.py", "args:hello.py,add.py,outfile="+cls.logFile] + cls.lines2Msg = "hello.py is 16 lines longadd.py is 18 lines long" + cls.linesUnreadable = ["lines.py", "args:hello.py,add.py,1,2,outfile="+cls.logFile] + cls.linesUnreadableMsg = "hello.py is 16 lines longadd.py is 18 lines longFile '1' cannot be readFile '2' cannot be read" + + # + @classmethod + def tearDownClass(cls): + pass + # + def setUp(self): + self.removeLogFile() + # + def tearDown(self): + self.removeLogFile() + # + def session(self, args=[]): + self.SALOME.main(self.SALOME_args + args) + # + def removeLogFile(self): + try: + os.remove(self.logFile) + except OSError: + pass + # + def assertLogFileContentsEqual(self, message): + with open(self.logFile, "r") as f: + contents = f.read().replace('\n', '') + + #sys.stderr.write("Generated contents :%s\n"%contents) + #sys.stderr.write("Expected contents :%s\n"%message) + self.assertTrue(contents==message) + # + def testHello0(self): + self.session(self.hello0) + self.assertLogFileContentsEqual(self.hello0Msg) + # + def testPythonHello0(self): + self.session(["python"]+self.hello0) + self.assertLogFileContentsEqual(self.hello0Msg) + # + def testHello1(self): + self.session(self.hello1) + self.assertLogFileContentsEqual(self.hello1Msg) + # + def testAdd0(self): + self.session(self.add0) + self.assertLogFileContentsEqual(self.add0Msg) + # + def testAdd3(self): + self.session(self.add3) + self.assertLogFileContentsEqual(self.add3Msg) + # + def testHello0Add3(self): + self.session(self.hello0+self.add3) + self.assertLogFileContentsEqual(self.hello0Msg+self.add3Msg) + # + def testHello1Add3(self): + self.session(self.hello1+self.add3) + self.assertLogFileContentsEqual(self.hello1Msg+self.add3Msg) + # + def testHelloToAdd(self): + self.session(self.helloToAdd) + self.assertLogFileContentsEqual(self.helloToAddMsg) + # + def testLines0(self): + self.session(self.lines0) + self.assertLogFileContentsEqual(self.lines0Msg) + # + def testLines2(self): + self.session(self.lines2) + self.assertLogFileContentsEqual(self.lines2Msg) + # + def testLines2Add3(self): + self.session(self.lines2+self.add3) + self.assertLogFileContentsEqual(self.lines2Msg+self.add3Msg) + # + def testLinesUnreadable(self): + self.session(self.linesUnreadable) + self.assertLogFileContentsEqual(self.linesUnreadableMsg) + # + def testAddAddHello(self): + self.session(self.add3+self.add3+self.hello1) + self.assertLogFileContentsEqual(self.add3Msg+self.add3Msg+self.hello1Msg) + # + def testHello0Add3Hello0Add3Hello0(self): + self.session(self.hello1+self.add3+self.hello0+self.add3+self.hello0) + self.assertLogFileContentsEqual(self.hello1Msg+self.add3Msg+self.hello0Msg+self.add3Msg+self.hello0Msg) + # +# + + +if __name__ == "__main__": + unittest.main() diff --git a/bin/appliskel/tests/launcher/add.py b/bin/appliskel/tests/launcher/add.py new file mode 100755 index 000000000..0f55d925f --- /dev/null +++ b/bin/appliskel/tests/launcher/add.py @@ -0,0 +1,18 @@ +#! /usr/bin/env python + +import sys +import logging + +from getLogger import getLogger + +if __name__ == "__main__": + args = sys.argv[1:] + logger, args = getLogger(args) + + if len(args)==0: + logger.info("No args!") + else: + msg = "+".join(args) + res = sum(map(int, args)) + logger.info("%s = %s"%(msg, res)) +# diff --git a/bin/appliskel/tests/launcher/getLogger.py b/bin/appliskel/tests/launcher/getLogger.py new file mode 100644 index 000000000..ade760106 --- /dev/null +++ b/bin/appliskel/tests/launcher/getLogger.py @@ -0,0 +1,24 @@ +import os +import sys +import logging + +def getLogger(args=[]): + outfileOptionPrefix = "outfile=" + outfileArgs = [ str(x) for x in args if str(x).startswith(outfileOptionPrefix) ] + allFiles = [ x.replace(outfileOptionPrefix, '') for x in outfileArgs ] + args = [ x for x in args if not str(x).startswith(outfileOptionPrefix) ] + + logger = logging.getLogger(__name__) + if len(allFiles) == 0: + logger.addHandler(logging.StreamHandler()) + else: + for currentFile in allFiles: + elements = currentFile.split(',') + for elt in elements: + elt = os.path.realpath(os.path.expanduser(elt)) + hdlr = logging.FileHandler(elt) + logger.addHandler(hdlr) + # + logger.level = logging.DEBUG + return logger, args +# diff --git a/bin/appliskel/tests/launcher/hello.py b/bin/appliskel/tests/launcher/hello.py new file mode 100755 index 000000000..cda6042f8 --- /dev/null +++ b/bin/appliskel/tests/launcher/hello.py @@ -0,0 +1,16 @@ +#! /usr/bin/env python + +import sys +import logging + +from getLogger import getLogger + +if __name__ == "__main__": + args = sys.argv[1:] + logger, args = getLogger(args) + + if len(args)==0: + logger.info("Hello!") + else: + logger.info("Hello to: %s"%(", ".join(args))) +# diff --git a/bin/appliskel/tests/launcher/lines.py b/bin/appliskel/tests/launcher/lines.py new file mode 100755 index 000000000..38b7cdac7 --- /dev/null +++ b/bin/appliskel/tests/launcher/lines.py @@ -0,0 +1,29 @@ +#! /usr/bin/env python + +import sys +import logging + +from getLogger import getLogger + + +def file_len(fname): + with open(fname) as f: + for i, l in enumerate(f): + pass + return i + 1 +# + +if __name__ == "__main__": + args = sys.argv[1:] + logger, args = getLogger(args) + + if len(args)==0: + logger.info("No files given") + else: + for filename in args: + try: + nb = file_len(filename) + logger.info("%s is %s lines long"%(filename, nb)) + except IOError: + logger.info("File '%s' cannot be read"%(filename)) +# diff --git a/bin/launchConfigureParser.py b/bin/launchConfigureParser.py index 43c334e41..4bbc9d60d 100755 --- a/bin/launchConfigureParser.py +++ b/bin/launchConfigureParser.py @@ -28,6 +28,8 @@ import types from salome_utils import verbose, setVerbose, getPortNumber, getHomeDir +from salomeLauncherUtils import getScriptsAndArgs + # names of tags in XML configuration file doc_tag = "document" sec_tag = "section" @@ -537,21 +539,6 @@ def CreateOptionParser (theAdditionalOptions=[]): dest="log_file", help=help_str) - # Execute python scripts. Default: None. - help_str = "Python script(s) to be imported. Python scripts are imported " - help_str += "in the order of their appearance. In GUI mode python scripts " - help_str += "are imported in the embedded python interpreter of current study, " - help_str += "otherwise in an external python interpreter. " - help_str += "Note: this option is obsolete. Instead you can pass Python script(s) " - help_str += "directly as positional parameter." - o_u = optparse.Option("-u", - "--execute", - metavar="", - type="string", - action="append", - dest="py_scripts", - help=help_str) - # Configuration XML file. Default: see defaultUserFile() function help_str = "Parse application settings from the " help_str += "instead of default %s" % defaultUserFile() @@ -791,7 +778,6 @@ def CreateOptionParser (theAdditionalOptions=[]): o_d,o_o, # Desktop o_b, # Batch o_l,o_f, # Use logger or log-file - o_u, # Execute python scripts o_r, # Configuration XML file o_x, # xterm o_m, # Modules @@ -818,14 +804,16 @@ def CreateOptionParser (theAdditionalOptions=[]): o_port, # Use port ] - #std_options = ["gui", "desktop", "log_file", "py_scripts", "resources", + #std_options = ["gui", "desktop", "log_file", "resources", # "xterm", "modules", "embedded", "standalone", # "portkill", "killall", "interp", "splash", # "catch_exceptions", "print_port", "save_config", "ns_port_log_file"] opt_list += theAdditionalOptions - a_usage = "%prog [options] [STUDY_FILE] [PYTHON_FILE [PYTHON_FILE ...]]" + a_usage = """%prog [options] [STUDY_FILE] [PYTHON_FILE [args] [PYTHON_FILE [args]...]] +Python file arguments, if any, must be comma-separated (without blank characters) and prefixed by "args:" (without quotes), e.g. myscript.py args:arg1,arg2=val,... +""" version_str = "Salome %s" % version() pars = optparse.OptionParser(usage=a_usage, version=version_str, option_list=opt_list) @@ -984,7 +972,7 @@ def get_env(theAdditionalOptions=[], appname=salomeappname, cfgname=salomecfgnam # apply command-line options to the arguments # each option given in command line overrides the option from xml config file # - # Options: gui, desktop, log_file, py_scripts, resources, + # Options: gui, desktop, log_file, resources, # xterm, modules, embedded, standalone, # portkill, killall, interp, splash, # catch_exceptions, pinter @@ -1024,22 +1012,14 @@ def get_env(theAdditionalOptions=[], appname=salomeappname, cfgname=salomecfgnam if cmd_opts.ns_port_log_file is not None: args["ns_port_log_file"] = cmd_opts.ns_port_log_file - # Python scripts - args[script_nam] = [] - if cmd_opts.py_scripts is not None: - listlist = cmd_opts.py_scripts - for listi in listlist: - if os.sys.platform == 'win32': - args[script_nam] += re.split( "[;,]", listi) - else: - args[script_nam] += re.split( "[:;,]", listi) - for arg in cmd_args: - if arg[-3:] == ".py": - args[script_nam].append(arg) - elif not args["study_hdf"]: + # Study files + if len(cmd_args) > 0 and not args["study_hdf"]: + arg = cmd_args[0] # :NOTE: only look at first element + if arg[-4:] == ".hdf": args["study_hdf"] = arg - pass - pass + + # Python scripts + args[script_nam] = getScriptsAndArgs(cmd_args) # xterm if cmd_opts.xterm is not None: args[xterm_nam] = cmd_opts.xterm diff --git a/bin/parseConfigFile.py b/bin/parseConfigFile.py index 65219d373..76c014c8f 100644 --- a/bin/parseConfigFile.py +++ b/bin/parseConfigFile.py @@ -208,7 +208,7 @@ class EnvFileConverter(object): self.outputFile = outputFile self.allParsedVariableNames=[] # exclude line that begin with: - self.exclude = [ 'if', 'then', 'fi', '#', 'echo' ] + self.exclude = [ 'if', 'then', 'else', 'fi', '#', 'echo' ] # discard the following keywords if at the beginning of line: self.discard = [ 'export' ] # the following keywords imply a special processing if at the beginning of line: @@ -305,7 +305,7 @@ class EnvFileConverter(object): # Convert .sh environment file to configuration file format def convertEnvFileToConfigFile(envFilename, configFilename): #reserved=['PATH', 'LD_LIBRARY_PATH', 'PYTHONPATH'] - print "convert envFilename",envFilename,"to",configFilename + logConfigParser.debug('convert env file %s to %s'%(envFilename, configFilename)) reserved=['PATH', 'LD_LIBRARY_PATH', 'PYTHONPATH', 'MANPATH', 'R_LIBS', 'PV_PLUGIN_PATH', 'TCLLIBPATH', 'TKLIBPATH'] fileContents = open(envFilename, 'r').read() diff --git a/bin/runConsole.py b/bin/runConsole.py index 72d73c77b..a6e400142 100644 --- a/bin/runConsole.py +++ b/bin/runConsole.py @@ -1,5 +1,5 @@ # -*- coding: iso-8859-1 -*- -# Copyright (C) 2007-2012 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2013 CEA/DEN, EDF R&D, OPEN CASCADE # # Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, # CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS @@ -21,16 +21,18 @@ # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # -def __prompt(vars = None, commands=[], message = "Connecting to SALOME"): - if vars is None: - vars = globals() +def __prompt(environment = None, commands=[], message = "Connecting to SALOME"): + if environment is None: + environment = globals().copy() + environment.update(locals()) + import code import rlcompleter import readline readline.parse_and_bind("tab: complete") # calling this with globals ensures we can see the environment print message - shell = code.InteractiveConsole(vars) + shell = code.InteractiveConsole(environment) for cmd in commands: shell.push(cmd) return shell.interact diff --git a/bin/runSalome.py b/bin/runSalome.py index 7e3d62634..1c6791c82 100755 --- a/bin/runSalome.py +++ b/bin/runSalome.py @@ -31,6 +31,9 @@ import orbmodule import setenv from launchConfigureParser import verbose from server import process_id, Server +import json +from salomeLauncherUtils import formatScriptsAndArgs +import subprocess # ----------------------------------------------------------------------------- @@ -261,7 +264,8 @@ class SessionServer(Server): pass pass if self.args.has_key('pyscript') and len(self.args['pyscript']) > 0: - self.SCMD2+=['--pyscript=%s'%(",".join(self.args['pyscript']))] + msg = json.dumps(self.args['pyscript']) + self.SCMD2+=['--pyscript=%s'%(msg)] pass pass pass @@ -724,39 +728,11 @@ def useSalome(args, modules_list, modules_root_dir): if not args['gui'] or not args['session_gui']: toimport = args['pyscript'] - for srcname in toimport : - if srcname == 'killall': - clt.showNS() - killAllPorts() - sys.exit(0) - else: - if os.path.isabs(srcname): - if os.path.exists(srcname): - execScript(srcname) - elif os.path.exists(srcname+".py"): - execScript(srcname+".py") - else: - print "Can't execute file %s" % srcname - pass - else: - found = False - for path in [os.getcwd()] + sys.path: - if os.path.exists(os.path.join(path,srcname)): - execScript(os.path.join(path,srcname)) - found = True - break - elif os.path.exists(os.path.join(path,srcname+".py")): - execScript(os.path.join(path,srcname+".py")) - found = True - break - pass - if not found: - print "Can't execute file %s" % srcname - pass - pass - pass - pass - pass + command = formatScriptsAndArgs(toimport) + if command: + proc = subprocess.Popen(command, shell=True) + proc.wait() + return clt def execScript(script_path): diff --git a/bin/runSession.py b/bin/runSession.py index 07665fdd5..e3b5f0857 100644 --- a/bin/runSession.py +++ b/bin/runSession.py @@ -33,8 +33,12 @@ class MyParser(OptionParser): # def configureSession(args=[]): - usage = "Usage: %prog [options]" - epilog = """\nIf the command is not given a shell is opened. + usage = "Usage: %prog [options] [command]" + epilog = """\n +If the command is not given a shell is opened; else execute the given command. +Command may be a series of Python scripts with arguments: [PYTHON_FILE [args] [PYTHON_FILE [args]...]] +Python file arguments, if any, must be comma-separated (without blank characters) and prefixed by "args:" (without quotes), e.g. myscript.py args:arg1,arg2=val,... +\n If PORT and MACHINE are not given, try to connect to the last active session on the local machine. If PORT and MACHINE are given, try to connect to the remote session associated with PORT on MACHINE. If MACHINE is not given, try to connect to the session associated to PORT on the local machine. diff --git a/bin/salomeConsole.py b/bin/salomeConsole.py index 0a80ae662..46036d3e7 100755 --- a/bin/salomeConsole.py +++ b/bin/salomeConsole.py @@ -24,6 +24,8 @@ import os import sys +import glob + #------------------------------- # Python completion and others if you want # You should have set PYTHONSTARTUP env variable @@ -36,56 +38,41 @@ import user #------------------------------- import CORBA import CosNaming -# There are cyclic dependencies between Engines, SALOME and SALOMEDS. -# import first Engines, then SALOME and then SALOMEDS -# Or use reload(Engines) to be safe. -import Engines -import SALOME -import SALOMEDS -import SALOME_ModuleCatalog -reload(Engines) -reload(SALOME) -reload(SALOMEDS) import salome_utils -import LifeCycleCORBA import orbmodule -from runSalome import * -omniorbUserPath = os.getenv("OMNIORB_USER_PATH") -files = glob.glob(os.path.join(omniorbUserPath,".omniORB_"+salome_utils.getUserName()+"_*.cfg")) +def getRunningSession(): + omniorbUserPath = os.getenv("OMNIORB_USER_PATH") + files = glob.glob(os.path.join(omniorbUserPath,".omniORB_"+salome_utils.getUserName()+"_*.cfg")) -filename="" -if len(files)==1: - filename=files[0] -else: - print "You have %d sessions running" % len(files) - for f in files: - print "Session:",f - rep= raw_input("Do you want to connect to this session [y|n]") - if rep == "y": + filename="" + if len(files)==1: + filename=files[0] + else: + print "You have %d sessions running" % len(files) + for f in files: + print "Session:",f + rep= raw_input("Do you want to connect to this session [y|n]") + if rep == "y": filename=f break -if filename != "": - os.environ['OMNIORB_CONFIG']=filename -else: - rep= raw_input("Do you want to try a local session on port 2810 ? [y|n]") - if rep == "y": - # Try a local session running on port 2810 - sys.argv=sys.argv+['-ORBInitRef','NameService=corbaname::localhost:2810']#+['-ORBgiopMaxMsgSize','2097152000'] # 2 GBytes + if filename != "": + os.environ['OMNIORB_CONFIG']=filename else: - sys.exit(1) - -#print sys.argv - -#direct adress from clt.orb.object_to_string(clt.rootContext) -#sys.argv=sys.argv+['-ORBInitRef','NameService=IOR:010000000100000000000000010000000000000023000000010100000a0000006c6f63616c686f737400fa0a0b0000004e616d6553657276696365'] + rep= raw_input("Do you want to try a local session on port 2810 ? [y|n]") + if rep == "y": + # Try a local session running on port 2810 + sys.argv=sys.argv+['-ORBInitRef','NameService=corbaname::localhost:2810'] + else: + sys.exit(1) +# class client(orbmodule.client): def initNS(self,args): # Obtain a reference to the root naming context - obj = self.orb.resolve_initial_references("NameService") + obj = self.orb.resolve_initial_references("NameService") try: self.rootContext = obj._narrow(CosNaming.NamingContext) return @@ -93,15 +80,25 @@ class client(orbmodule.client): print "It's not a valid naming service" self.rootContext = None raise +# -clt=client() -print "Naming Service address: ",clt.orb.object_to_string(clt.rootContext) +def startClient(): + try: + clt=client() + except Exception: + sys.exit(1) + # + print "Naming Service address: ",clt.orb.object_to_string(clt.rootContext) -clt.showNS() + clt.showNS() + + session=clt.waitNS("/Kernel/Session") + catalog=clt.waitNS("/Kernel/ModulCatalog") + studyMgr=clt.waitNS("/myStudyManager") + import salome + salome.salome_init() + from salome import lcc +# -session=clt.waitNS("/Kernel/Session") -catalog=clt.waitNS("/Kernel/ModulCatalog") -studyMgr=clt.waitNS("/myStudyManager") -import salome -salome.salome_init() -from salome import lcc +getRunningSession() +startClient() diff --git a/bin/salomeLauncherUtils.py b/bin/salomeLauncherUtils.py new file mode 100644 index 000000000..6d26e9c01 --- /dev/null +++ b/bin/salomeLauncherUtils.py @@ -0,0 +1,112 @@ +#! /usr/bin/env python + +import os +import sys +import glob +import subprocess + +""" +Define a specific exception class to manage exceptions related to SalomeRunner +""" +class SalomeRunnerException(Exception): + """Report error messages to the user interface of SalomeRunner.""" +# + +def __listDirectory(path): + allFiles = [] + for root, dirs, files in os.walk(path): + configFileNames = glob.glob(os.path.join(root,'*.cfg')) + glob.glob(os.path.join(root,'*.sh')) + allFiles += configFileNames + return allFiles +# + +def __getConfigFileNamesDefault(): + absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','') + if not absoluteAppliPath: + return [] + + envdDir = absoluteAppliPath + '/env.d' + if not os.path.isdir(envdDir): + return [] + + return __listDirectory(envdDir) +# + +def getConfigFileNames(args): + # special case: configuration files are provided by user + # Search for command-line argument(s) --config=file1,file2,..., filen + # Search for command-line argument(s) --config=dir1,dir2,..., dirn + configOptionPrefix = "--config=" + configArgs = [ str(x) for x in args if str(x).startswith(configOptionPrefix) ] + + if len(configArgs) == 0: + return __getConfigFileNamesDefault(), args + + args = [ x for x in args if not x.startswith(configOptionPrefix) ] + allLists = [ x.replace(configOptionPrefix, '') for x in configArgs ] + + configFileNames = [] + for currentList in allLists: + elements = currentList.split(',') + for elt in elements: + elt = os.path.realpath(os.path.expanduser(elt)) + if os.path.isdir(elt): + configFileNames += __listDirectory(elt) + else: + configFileNames += [elt] + + return configFileNames, args +# + +# Return an array of dictionaries {script_name: [list_of_its_args]} +def getScriptsAndArgs(args=[]): + # Syntax of args: script.py [args:a1,a2=val,an] ... script.py [args:a1,a2=val,an] + scriptArgs = [] + currentScript = None + argsPrefix = "args:" + callPython = False + + for i in range(len(args)): + elt = args[i] + + if elt.startswith(argsPrefix): + if not currentScript or callPython: + raise SalomeRunnerException("args list must follow corresponding script file in command line.") + elt = elt.replace(argsPrefix, '') + scriptArgs[len(scriptArgs)-1][currentScript] = elt.split(",") + currentScript = None + callPython = False + elif elt.startswith("python"): + callPython = True + elif os.path.isfile(elt) or os.path.isfile(elt+".py"): + if elt[-3:] == ".py": + currentScript = os.path.abspath(elt) + else: + currentScript = os.path.abspath(elt+".py") + if callPython: + scriptArgs.append({"python "+currentScript:[]}) + callPython = False + else: + if not os.access(currentScript, os.X_OK): + raise SalomeRunnerException("Argument %s cannot be executed (please check file permissions)"%currentScript) + scriptArgs.append({currentScript:[]}) + else: + raise SalomeRunnerException("Incorrect syntax in command line: %s:\n\t%s"%(elt," ".join(args))) + # end for loop + return scriptArgs +# + +# Formatting scripts and args as a Bash-like command-line: +# script1.py [args] ; script2.py [args] ; ... +def formatScriptsAndArgs(scriptArgs=[]): + commands = [] + for sc_dict in scriptArgs: + for script, sc_args in sc_dict.items(): # single entry + cmd = script + if sc_args: + cmd = cmd + " " + " ".join(sc_args) + commands.append(cmd) + + command = "; ".join(["%s"%x for x in commands]) + return command +# diff --git a/bin/salomeRunner.py b/bin/salomeRunner.py index ab60f5d4b..875e914d8 100644 --- a/bin/salomeRunner.py +++ b/bin/salomeRunner.py @@ -11,14 +11,8 @@ import pickle import subprocess import platform - -""" -Define a specific exception class to manage exceptions related to SalomeRunner -""" -class SalomeRunnerException(Exception): - """Report error messages to the user interface of SalomeRunner.""" -# - +from salomeLauncherUtils import SalomeRunnerException +from salomeLauncherUtils import getScriptsAndArgs, formatScriptsAndArgs """ The SalomeRunner class in an API to configure SALOME environment then @@ -38,7 +32,7 @@ class SalomeRunner: #it could be None explicitely (if user use multiples setEnviron...for standalone) if configFileNames==None: return - + if len(configFileNames) == 0: raise SalomeRunnerException("No configuration files given") @@ -48,8 +42,8 @@ class SalomeRunner: self.__setEnvironmentFromConfigFile(filename) elif extension == ".sh": #new convert procedures, temporary could be use not to be automatically deleted - temp = tempfile.NamedTemporaryFile(suffix='.cfg', delete=False) - #temp = tempfile.NamedTemporaryFile(suffix='.cfg') + #temp = tempfile.NamedTemporaryFile(suffix='.cfg', delete=False) + temp = tempfile.NamedTemporaryFile(suffix='.cfg') try: convertEnvFileToConfigFile(filename, temp.name) self.__setEnvironmentFromConfigFile(temp.name) @@ -98,14 +92,14 @@ class SalomeRunner: # """Set environment variable to value""" - def setEnviron(self, name, value, overwrite=True): + def setEnviron(self, name, value, overwrite=False): env = os.getenv(name, '') if env and not overwrite: - self.getLogger().warning("Environment variable already existing (and not overwritten): %s, %s", name, value) + self.getLogger().warning("Environment variable already existing (and not overwritten): %s=%s", name, value) return if env: - self.getLogger().warning("Environment variable overwriting: %s, %s", name, value) + self.getLogger().warning("Overwriting environment variable: %s=%s", name, value) value = os.path.expandvars(value) # expand environment variables self.getLogger().debug("Set environment variable: %s=%s", name, value) @@ -123,10 +117,10 @@ class SalomeRunner: ################################### def _usage(self, unused=[]): - exeName = os.path.splitext(os.path.basename(__file__))[0] + #exeName = os.path.splitext(os.path.basename(__file__))[0] msg = '''\ -Usage: %s [command] [options] [--config=file1,...,filen] +Usage: salome [command] [options] [--config=file1,...,filen] Commands: start Launches SALOME virtual application [DEFAULT] @@ -137,7 +131,7 @@ Commands: help Show this message coffee Yes! SALOME can also make coffee!!"\ -'''%exeName +''' print msg # @@ -160,9 +154,8 @@ Commands: } if not command in availableCommands.keys(): - self.getLogger().error("Unrecognized command: %s.", command) - self._usage() - sys.exit(1) + command = "start" + options = args return availableCommands[command], options # @@ -184,21 +177,22 @@ Commands: command = "_runAppli" try: - getattr(self, command)(options) # run appropriate method - except AttributeError: - self.getLogger().error("Method %s is not implemented.", command) - sys.exit(1) + res = getattr(self, command)(options) # run appropriate method + return res or (None, None) except SystemExit, exc: if exc==0: sys.exit(0) #catch sys.exit(0) happy end no warning if exc==1: self.getLogger().warning("SystemExit 1 in method %s.", command) sys.exit(1) - except: + except StandardError: self.getLogger().error("Unexpected error:") import traceback traceback.print_exc() sys.exit(1) + except SalomeRunnerException, e: + self.getLogger().error(e) + sys.exit(1) # def __setEnvironmentFromConfigFile(self, filename): @@ -268,17 +262,11 @@ Commands: import setenv setenv.main(True) - if args: - exe = args[0] - # if exe does not contain any slashes (/), search in PATH - # if exe contains slashes: - # if exe begins with a slash, use this absolute path - # else build absolute path relative to current working directory - if (os.sep in exe) and (exe[0] is not os.sep): - args[0] = os.getcwd() + os.sep + exe - - proc = subprocess.Popen(args, shell=False, close_fds=True) - proc.wait() + scriptArgs = getScriptsAndArgs(args) + command = formatScriptsAndArgs(scriptArgs) + if command: + proc = subprocess.Popen(command, shell=True, close_fds=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + return proc.communicate() else: absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','') cmd = ["/bin/bash", "--rcfile", absoluteAppliPath + "/.bashrc" ] @@ -297,7 +285,6 @@ Commands: # def _killAll(self, args=[]): - #self._runAppli(['-k'] + args) from killSalome import killAllPorts killAllPorts() # @@ -349,6 +336,7 @@ Commands: if not hasattr(self, '_logger'): self._logger = logging.getLogger(__name__) #self._logger.setLevel(logging.DEBUG) + self._logger.setLevel(logging.ERROR) return self._logger; # @@ -358,7 +346,11 @@ if __name__ == "__main__": if len(sys.argv) == 3: runner = pickle.loads(sys.argv[1]) args = pickle.loads(sys.argv[2]) - runner._getStarted(args) + (out, err) = runner._getStarted(args) + if out: + sys.stdout.write(out) + if err: + sys.stderr.write(err) else: SalomeRunner()._usage() # -- 2.39.2