3 # Copyright (C) 2013-2016 CEA/DEN, EDF R&D, OPEN CASCADE
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2.1 of the License, or (at your option) any later version.
10 # This library is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # Lesser General Public License for more details.
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
31 Define a specific exception class to manage exceptions related to SalomeContext
33 class SalomeContextException(Exception):
34 """Report error messages to the user interface of SalomeContext."""
37 def __listDirectory(path):
39 for root, dirs, files in os.walk(path):
40 cfgFiles = glob.glob(os.path.join(root,'*.cfg'))
43 shFiles = glob.glob(os.path.join(root,'*.sh'))
45 no_ext = os.path.splitext(f)[0]
46 if not os.path.isfile(no_ext+".cfg"):
52 def __getConfigFileNamesDefault():
53 absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
54 if not absoluteAppliPath:
57 envdDir = absoluteAppliPath + '/env.d'
58 if not os.path.isdir(envdDir):
61 return __listDirectory(envdDir)
64 def __getEnvironmentFileNames(args, optionPrefix, checkExistence):
65 # special case: extra configuration/environment files are provided by user
66 # Search for command-line argument(s) <optionPrefix>=file1,file2,..., filen
67 # Search for command-line argument(s) <optionPrefix>=dir1,dir2,..., dirn
68 configArgs = [ str(x) for x in args if str(x).startswith(optionPrefix) ]
70 args = [ x for x in args if not x.startswith(optionPrefix) ]
71 allLists = [ x.replace(optionPrefix, '') for x in configArgs ]
75 for currentList in allLists:
76 elements = currentList.split(',')
78 elt = os.path.realpath(os.path.expanduser(elt))
79 if os.path.isdir(elt):
80 configFileNames += __listDirectory(elt)
82 if checkExistence and not os.path.isfile(elt):
85 configFileNames += [elt]
87 return configFileNames, args, unexisting
90 def __validate_pair(ob):
92 if not (len(ob) == 2):
93 #print "Unexpected result:", ob
99 def __get_environment_from_batch_command(env_cmd, initial=None):
101 Take a command (either a single command or list of arguments)
102 and return the environment created after running that command.
103 Note that if the command must be a batch file or .cmd file, or the
104 changes to the environment will not be captured.
106 If initial is supplied, it is used as the initial environment passed
107 to the child process.
109 #if not isinstance(env_cmd, (list, tuple)):
110 # env_cmd = [env_cmd]
111 # construct the command that will alter the environment
112 #env_cmd = subprocess.list2cmdline(env_cmd)
113 # create a tag so we can tell in the output when the proc is done
114 tag = 'Done running command'
115 # construct a command to do accomplish this
116 cmd = '{env_cmd} && echo "{tag}"'.format(**vars())
119 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, env=initial, shell=True)
120 # parse the output sent to stdout
122 # consume whatever output occurs until the tag is reached
123 #consume(itertools.takewhile(lambda l: tag not in l, lines))
124 # define a way to handle each KEY=VALUE line
125 handle_line = lambda l: l.rstrip().split('=',1)
126 # parse key/values into pairs
127 #pairs = map(handle_line, lines)
131 line = lines.readline()
133 if tag in line or cpt > 1000:
136 pairs.append(line.rstrip().split('=',1))
137 # make sure the pairs are valid
138 valid_pairs = filter(__validate_pair, pairs)
139 # construct a dictionary of the pairs
140 result = dict(valid_pairs)
141 # let the process finish
145 def __subtract(ref, dic):
147 for key,val in ref.items():
148 if not dic.has_key(key):
151 # compare values types
152 if (type(dic[key]) != type(val)):
156 if isinstance(val, basestring):
157 tolist1 = dic[key].split(os.pathsep)
158 tolist2 = val.split(os.pathsep)
159 diff = list(set(tolist2)-set(tolist1))
161 result[key] = os.pathsep.join(diff)
168 def getConfigFileNames(args, checkExistence=False):
169 configOptionPrefix = "--config="
170 configArgs = [ str(x) for x in args if str(x).startswith(configOptionPrefix) ]
171 if len(configArgs) == 0:
172 configFileNames, unexist1 = __getConfigFileNamesDefault(), []
174 # get configuration filenames
175 configFileNames, args, unexist1 = __getEnvironmentFileNames(args, configOptionPrefix, checkExistence)
177 # get extra environment
178 extraEnvFileNames, args, unexist2 = __getEnvironmentFileNames(args, "--extra_env=", checkExistence)
179 before = __get_environment_from_batch_command("env")
181 for filename in extraEnvFileNames:
182 after.update(__get_environment_from_batch_command(filename))
185 extraEnv = __subtract(after,before)
186 return configFileNames, extraEnv, args, unexist1+unexist2
189 def __getScriptPath(scriptName, searchPathList):
190 scriptName = os.path.expanduser(scriptName)
191 if os.path.isabs(scriptName):
194 if searchPathList is None or len(searchPathList) == 0:
197 for path in searchPathList:
198 fullName = os.path.join(path, scriptName)
199 if os.path.isfile(fullName) or os.path.isfile(fullName+".py"):
206 # script: the command to be run, e.g. python <script.py>
207 # args: its input parameters
208 # out: its output parameters
209 def __init__(self, script=None, args=None, out=None):
215 msg = "\n# Script: %s\n"%self.script
216 msg += " * Input: %s\n"%self.args
217 msg += " * Output: %s\n"%self.out
221 class ScriptAndArgsObjectEncoder(json.JSONEncoder):
222 def default(self, obj):
223 if isinstance(obj, ScriptAndArgs):
224 # to be easily parsed in GUI module (SalomeApp_Application)
225 # Do not export output arguments
226 return {obj.script:obj.args or []}
228 return json.JSONEncoder.default(self, obj)
231 def getShortAndExtraArgs(args=None):
235 pos = args.index("--") # raise a ValueError if not found
236 short_args = args[:pos]
237 extra_args = args[pos:] # include "--"
243 return short_args, extra_args
246 # Return an array of ScriptAndArgs objects
247 def getScriptsAndArgs(args=None, searchPathList=None):
250 short_args, extra_args = getShortAndExtraArgs(args)
253 if searchPathList is None:
254 searchPathList = sys.path
256 # Syntax of args: script.py [args:a1,a2=val,an] ... script.py [args:a1,a2=val,an]
265 for i in range(len(args)):
266 elt = os.path.expanduser(args[i])
267 isDriver = (elt == "driver") # special case for YACS scheme execution
269 if elt.startswith(argsPrefix):
270 if not currentKey or callPython:
271 raise SalomeContextException("args list must follow corresponding script file in command line.")
272 elt = elt.replace(argsPrefix, '')
273 # Special process if some items of 'args:' list are themselves lists
274 # Note that an item can be a list, but not a list of lists...
275 # So we can have something like this:
276 # myscript.py args:['file1','file2'],val1,"done",[1,2,3],[True,False],"ok"
277 # With such a call, an elt variable contains the string representing ['file1','file2'],val1,"done",[1,2,3],[True,False],"ok" that is '[file1,file2],val1,done,[1,2,3],[True,False],ok'
278 # We have to split elt to obtain: ['[file1,file2]','val1','done','[1,2,3]','[True,False]','ok']
279 contains_list = re.findall('(\[[^\]]*\])', elt)
283 # x is ['[file1', 'file2]', 'val1', 'done', '[1', '2', '3]', '[True', 'False]', 'ok']
284 list_begin_indices = [i for i in xrange(len(x)) if x[i].startswith('[')] # [0, 4, 7]
285 list_end_indices = [i for i in xrange(len(x)) if x[i].endswith(']')] # [1, 6, 8]
287 for lbeg, lend in zip(list_begin_indices,list_end_indices): # [(0, 1), (4, 6), (7, 8)]
289 extracted_args += x[start:lbeg]
291 extracted_args += [','.join(x[lbeg:lend+1])]
295 extracted_args += x[start:len(x)]
297 scriptArgs[len(scriptArgs)-1].args = extracted_args
299 else: # a single split is enough
300 scriptArgs[len(scriptArgs)-1].args = [os.path.expanduser(x) for x in elt.split(",")]
305 elif elt.startswith(outPrefix):
306 if (not currentKey and not afterArgs) or callPython:
307 raise SalomeContextException("out list must follow both corresponding script file and its args in command line.")
308 elt = elt.replace(outPrefix, '')
309 scriptArgs[len(scriptArgs)-1].out = [os.path.expanduser(x) for x in elt.split(",")]
313 elif elt.startswith("python"):
317 if not os.path.isfile(elt) and not os.path.isfile(elt+".py"):
318 eltInSearchPath = __getScriptPath(elt, searchPathList)
319 if eltInSearchPath is None or (not os.path.isfile(eltInSearchPath) and not os.path.isfile(eltInSearchPath+".py")):
320 if elt[-3:] == ".py":
321 raise SalomeContextException("Script not found: %s"%elt)
322 scriptArgs.append(ScriptAndArgs(script=elt))
324 elt = eltInSearchPath
326 if elt[-4:] != ".hdf":
327 if elt[-3:] == ".py" or isDriver:
328 currentScript = os.path.abspath(elt)
329 elif os.path.isfile(elt+".py"):
330 currentScript = os.path.abspath(elt+".py")
332 currentScript = os.path.abspath(elt) # python script not necessary has .py extension
335 if currentScript and callPython:
336 currentKey = "@PYTHONBIN@ "+currentScript
337 scriptArgs.append(ScriptAndArgs(script=currentKey))
341 currentKey = currentScript
342 scriptArgs.append(ScriptAndArgs(script=currentKey))
344 elif not os.access(currentScript, os.X_OK):
345 currentKey = "@PYTHONBIN@ "+currentScript
346 scriptArgs.append(ScriptAndArgs(script=currentKey))
350 fn = open(currentScript)
351 for i in xrange(10): # read only 10 first lines
353 if re.search("#!.*python"):
360 if not ispython and currentScript[-3:] == ".py":
361 currentKey = "@PYTHONBIN@ "+currentScript
363 currentKey = currentScript
365 scriptArgs.append(ScriptAndArgs(script=currentKey))
366 # CLOSE elif currentScript
370 if len(extra_args) > 1: # syntax: -- program [options] [arguments]
371 command = extra_args[1]
372 command_args = extra_args[2:]
373 scriptArgs.append(ScriptAndArgs(script=command, args=command_args))
379 # Formatting scripts and args as a Bash-like command-line:
380 # script1.py [args] ; script2.py [args] ; ...
381 # scriptArgs is a list of ScriptAndArgs objects; their output parameters are omitted
382 def formatScriptsAndArgs(scriptArgs=None):
383 if scriptArgs is None:
386 for sa_obj in scriptArgs:
389 cmd = " ".join([cmd]+sa_obj.args)
393 if sys.platform == "win32":
395 command = sep.join(["%s"%x for x in commands])
399 # Ensure OMNIORB_USER_PATH is defined. This variable refers to a folder in which
400 # SALOME will write omniOrb configuration files.
401 # If OMNIORB_USER_PATH is already set, only checks write access to associated directory ;
402 # an exception is raised if check fails. It allows users for choosing a specific folder.
403 # Else the function sets OMNIORB_USER_PATH this way:
404 # - If APPLI environment variable is set, OMNIORB_USER_PATH is set to ${APPLI}/USERS.
405 # The function does not check USERS folder existence or write access. This folder
406 # must exist ; this is the case if SALOME virtual application has been created using
407 # appli_gen.py script.
408 # - Else OMNIORB_USER_PATH is set to user home directory.
409 def setOmniOrbUserPath():
410 omniorbUserPath = os.getenv("OMNIORB_USER_PATH")
412 if not os.access(omniorbUserPath, os.W_OK):
413 raise Exception("Unable to get write access to directory: %s"%omniorbUserPath)
416 homePath = os.path.realpath(os.path.expanduser('~'))
417 #defaultOmniorbUserPath = os.path.join(homePath, ".salomeConfig/USERS")
418 defaultOmniorbUserPath = homePath
419 if os.getenv("APPLI"):
420 defaultOmniorbUserPath = os.path.join(homePath, os.getenv("APPLI"), "USERS")
422 os.environ["OMNIORB_USER_PATH"] = defaultOmniorbUserPath
426 return socket.gethostname().split('.')[0]