#! /usr/bin/env python
-# Copyright (C) 2013-2014 CEA/DEN, EDF R&D, OPEN CASCADE
+# Copyright (C) 2013-2016 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
import glob
import subprocess
import re
+import socket
+import json
"""
Define a specific exception class to manage exceptions related to SalomeContext
return __listDirectory(envdDir)
#
-def getConfigFileNames(args, checkExistence=False):
- # 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) ]
+def __getEnvironmentFileNames(args, optionPrefix, checkExistence):
+ # special case: extra configuration/environment files are provided by user
+ # Search for command-line argument(s) <optionPrefix>=file1,file2,..., filen
+ # Search for command-line argument(s) <optionPrefix>=dir1,dir2,..., dirn
+ configArgs = [ str(x) for x in args if str(x).startswith(optionPrefix) ]
- 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 ]
+ args = [ x for x in args if not x.startswith(optionPrefix) ]
+ allLists = [ x.replace(optionPrefix, '') for x in configArgs ]
configFileNames = []
unexisting = []
return configFileNames, args, unexisting
#
+def __validate_pair(ob):
+ try:
+ if not (len(ob) == 2):
+ #print "Unexpected result:", ob
+ raise ValueError
+ except:
+ return False
+ return True
+#
+def __get_environment_from_batch_command(env_cmd, initial=None):
+ """
+ Take a command (either a single command or list of arguments)
+ and return the environment created after running that command.
+ Note that if the command must be a batch file or .cmd file, or the
+ changes to the environment will not be captured.
+
+ If initial is supplied, it is used as the initial environment passed
+ to the child process.
+ """
+ #if not isinstance(env_cmd, (list, tuple)):
+ # env_cmd = [env_cmd]
+ # construct the command that will alter the environment
+ #env_cmd = subprocess.list2cmdline(env_cmd)
+ # create a tag so we can tell in the output when the proc is done
+ tag = 'Done running command'
+ # construct a command to do accomplish this
+ cmd = '{env_cmd} && echo "{tag}"'.format(**vars())
+
+ # launch the process
+ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, env=initial, shell=True)
+ # parse the output sent to stdout
+ lines = proc.stdout
+ # consume whatever output occurs until the tag is reached
+ #consume(itertools.takewhile(lambda l: tag not in l, lines))
+ # define a way to handle each KEY=VALUE line
+ handle_line = lambda l: l.rstrip().split('=',1)
+ # parse key/values into pairs
+ #pairs = map(handle_line, lines)
+ pairs = []
+ cpt = 0
+ while True:
+ line = lines.readline()
+ cpt = cpt+1
+ if tag in line or cpt > 1000:
+ break
+ if line:
+ pairs.append(line.rstrip().split('=',1))
+ # make sure the pairs are valid
+ valid_pairs = filter(__validate_pair, pairs)
+ # construct a dictionary of the pairs
+ result = dict(valid_pairs)
+ # let the process finish
+ proc.communicate()
+ return result
+#
+def __subtract(ref, dic):
+ result = {}
+ for key,val in ref.items():
+ if not dic.has_key(key):
+ result[key] = val
+ else:
+ # compare values types
+ if (type(dic[key]) != type(val)):
+ result[key] = val
+ else:
+ # compare values
+ if isinstance(val, basestring):
+ tolist1 = dic[key].split(os.pathsep)
+ tolist2 = val.split(os.pathsep)
+ diff = list(set(tolist2)-set(tolist1))
+ if diff:
+ result[key] = os.pathsep.join(diff)
+ else:
+ result[key] = val
+
+ return result
+#
+
+def getConfigFileNames(args, checkExistence=False):
+ configOptionPrefix = "--config="
+ configArgs = [ str(x) for x in args if str(x).startswith(configOptionPrefix) ]
+ if len(configArgs) == 0:
+ configFileNames, unexist1 = __getConfigFileNamesDefault(), []
+ else:
+ # get configuration filenames
+ configFileNames, args, unexist1 = __getEnvironmentFileNames(args, configOptionPrefix, checkExistence)
+
+ # get extra environment
+ extraEnvFileNames, args, unexist2 = __getEnvironmentFileNames(args, "--extra_env=", checkExistence)
+ before = __get_environment_from_batch_command("env")
+ after = {}
+ for filename in extraEnvFileNames:
+ after.update(__get_environment_from_batch_command(filename))
+ pass
+
+ extraEnv = __subtract(after,before)
+ return configFileNames, extraEnv, args, unexist1+unexist2
+#
+
def __getScriptPath(scriptName, searchPathList):
+ scriptName = os.path.expanduser(scriptName)
+ if os.path.isabs(scriptName):
+ return scriptName
+
if searchPathList is None or len(searchPathList) == 0:
return None
return None
#
-# Return an array of dictionaries {script_name: [list_of_its_args]}
-def getScriptsAndArgs(args=[], searchPathList=None):
+class ScriptAndArgs:
+ # script: the command to be run, e.g. python <script.py>
+ # args: its input parameters
+ # out: its output parameters
+ def __init__(self, script=None, args=None, out=None):
+ self.script = script
+ self.args = args
+ self.out = out
+ #
+ def __repr__(self):
+ msg = "\n# Script: %s\n"%self.script
+ msg += " * Input: %s\n"%self.args
+ msg += " * Output: %s\n"%self.out
+ return msg
+ #
+#
+class ScriptAndArgsObjectEncoder(json.JSONEncoder):
+ def default(self, obj):
+ if isinstance(obj, ScriptAndArgs):
+ # to be easily parsed in GUI module (SalomeApp_Application)
+ # Do not export output arguments
+ return {obj.script:obj.args or []}
+ else:
+ return json.JSONEncoder.default(self, obj)
+#
+
+def getShortAndExtraArgs(args=None):
+ if args is None:
+ args = []
+ try:
+ pos = args.index("--") # raise a ValueError if not found
+ short_args = args[:pos]
+ extra_args = args[pos:] # include "--"
+ except ValueError:
+ short_args = args
+ extra_args = []
+ pass
+
+ return short_args, extra_args
+#
+
+# Return an array of ScriptAndArgs objects
+def getScriptsAndArgs(args=None, searchPathList=None):
+ if args is None:
+ args = []
+ short_args, extra_args = getShortAndExtraArgs(args)
+ args = short_args
+
if searchPathList is None:
searchPathList = sys.path
scriptArgs = []
currentKey = None
argsPrefix = "args:"
+ outPrefix = "out:"
callPython = False
+ afterArgs = False
currentScript = None
for i in range(len(args)):
- elt = args[i]
+ elt = os.path.expanduser(args[i])
+ isDriver = (elt == "driver") # special case for YACS scheme execution
if elt.startswith(argsPrefix):
if not currentKey or callPython:
raise SalomeContextException("args list must follow corresponding script file in command line.")
elt = elt.replace(argsPrefix, '')
- scriptArgs[len(scriptArgs)-1][currentKey] = elt.split(",")
+ # Special process if some items of 'args:' list are themselves lists
+ # Note that an item can be a list, but not a list of lists...
+ # So we can have something like this:
+ # myscript.py args:['file1','file2'],val1,"done",[1,2,3],[True,False],"ok"
+ # 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'
+ # We have to split elt to obtain: ['[file1,file2]','val1','done','[1,2,3]','[True,False]','ok']
+ contains_list = re.findall('(\[[^\]]*\])', elt)
+ if contains_list:
+ extracted_args = []
+ x = elt.split(",")
+ # x is ['[file1', 'file2]', 'val1', 'done', '[1', '2', '3]', '[True', 'False]', 'ok']
+ list_begin_indices = [i for i in xrange(len(x)) if x[i].startswith('[')] # [0, 4, 7]
+ list_end_indices = [i for i in xrange(len(x)) if x[i].endswith(']')] # [1, 6, 8]
+ start = 0
+ for lbeg, lend in zip(list_begin_indices,list_end_indices): # [(0, 1), (4, 6), (7, 8)]
+ if lbeg > start:
+ extracted_args += x[start:lbeg]
+ pass
+ extracted_args += [','.join(x[lbeg:lend+1])]
+ start = lend+1
+ pass
+ if start < len(x):
+ extracted_args += x[start:len(x)]
+ pass
+ scriptArgs[len(scriptArgs)-1].args = extracted_args
+ pass
+ else: # a single split is enough
+ scriptArgs[len(scriptArgs)-1].args = [os.path.expanduser(x) for x in elt.split(",")]
+ pass
+ currentKey = None
+ callPython = False
+ afterArgs = True
+ elif elt.startswith(outPrefix):
+ if (not currentKey and not afterArgs) or callPython:
+ raise SalomeContextException("out list must follow both corresponding script file and its args in command line.")
+ elt = elt.replace(outPrefix, '')
+ scriptArgs[len(scriptArgs)-1].out = [os.path.expanduser(x) for x in elt.split(",")]
currentKey = None
callPython = False
+ afterArgs = False
elif elt.startswith("python"):
callPython = True
+ afterArgs = False
else:
if not os.path.isfile(elt) and not os.path.isfile(elt+".py"):
eltInSearchPath = __getScriptPath(elt, searchPathList)
if eltInSearchPath is None or (not os.path.isfile(eltInSearchPath) and not os.path.isfile(eltInSearchPath+".py")):
if elt[-3:] == ".py":
raise SalomeContextException("Script not found: %s"%elt)
+ scriptArgs.append(ScriptAndArgs(script=elt))
continue
elt = eltInSearchPath
if elt[-4:] != ".hdf":
- if elt[-3:] == ".py":
+ if elt[-3:] == ".py" or isDriver:
currentScript = os.path.abspath(elt)
elif os.path.isfile(elt+".py"):
currentScript = os.path.abspath(elt+".py")
else:
currentScript = os.path.abspath(elt) # python script not necessary has .py extension
pass
+
if currentScript and callPython:
currentKey = "@PYTHONBIN@ "+currentScript
- scriptArgs.append({currentKey:[]})
+ scriptArgs.append(ScriptAndArgs(script=currentKey))
callPython = False
elif currentScript:
- if not os.access(currentScript, os.X_OK):
+ if isDriver:
+ currentKey = currentScript
+ scriptArgs.append(ScriptAndArgs(script=currentKey))
+ callPython = False
+ elif not os.access(currentScript, os.X_OK):
currentKey = "@PYTHONBIN@ "+currentScript
- scriptArgs.append({currentKey:[]})
+ scriptArgs.append(ScriptAndArgs(script=currentKey))
else:
ispython = False
try:
else:
currentKey = currentScript
pass
- scriptArgs.append({currentKey:[]})
+ scriptArgs.append(ScriptAndArgs(script=currentKey))
+ # CLOSE elif currentScript
+ afterArgs = False
# end for loop
+
+ if len(extra_args) > 1: # syntax: -- program [options] [arguments]
+ command = extra_args[1]
+ command_args = extra_args[2:]
+ scriptArgs.append(ScriptAndArgs(script=command, args=command_args))
+ pass
+
return scriptArgs
#
# Formatting scripts and args as a Bash-like command-line:
# script1.py [args] ; script2.py [args] ; ...
-def formatScriptsAndArgs(scriptArgs=[]):
+# scriptArgs is a list of ScriptAndArgs objects; their output parameters are omitted
+def formatScriptsAndArgs(scriptArgs=None):
+ if scriptArgs is None:
+ return ""
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)
+ for sa_obj in scriptArgs:
+ cmd = sa_obj.script
+ if sa_obj.args:
+ cmd = " ".join([cmd]+sa_obj.args)
+ commands.append(cmd)
+
sep = " ; "
if sys.platform == "win32":
- sep= " & "
+ sep = " & "
command = sep.join(["%s"%x for x in commands])
return command
#
-# Ensure OMNIORB_USER_PATH is defined. This variable refers to a the folder in which
+# Ensure OMNIORB_USER_PATH is defined. This variable refers to a folder in which
# SALOME will write omniOrb configuration files.
# If OMNIORB_USER_PATH is already set, only checks write access to associated directory ;
# an exception is raised if check fails. It allows users for choosing a specific folder.
pass
os.environ["OMNIORB_USER_PATH"] = defaultOmniorbUserPath
#
+
+def getHostname():
+ return socket.gethostname().split('.')[0]
+#