Salome HOME
Merge Python 3 porting.
[modules/kernel.git] / bin / salomeContextUtils.py.in
1 #! /usr/bin/env python3
2
3 # Copyright (C) 2013-2016  CEA/DEN, EDF R&D, OPEN CASCADE
4 #
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.
9 #
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.
14 #
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
18 #
19 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 #
21
22 import os
23 import sys
24 import glob
25 import subprocess
26 import re
27 import socket
28 import json
29
30 """
31 Define a specific exception class to manage exceptions related to SalomeContext
32 """
33 class SalomeContextException(Exception):
34   """Report error messages to the user interface of SalomeContext."""
35 #
36
37 def __listDirectory(path):
38   allFiles = []
39   for root, dirs, files in os.walk(path):
40     cfgFiles = glob.glob(os.path.join(root,'*.cfg'))
41     allFiles += cfgFiles
42
43   return allFiles
44 #
45
46 def __getConfigFileNamesDefault():
47   absoluteAppliPath = os.getenv('ABSOLUTE_APPLI_PATH','')
48   if not absoluteAppliPath:
49     return []
50
51   envdDir = absoluteAppliPath + '/env.d'
52   if not os.path.isdir(envdDir):
53     return []
54
55   return __listDirectory(envdDir)
56 #
57
58 def __getEnvironmentFileNames(args, optionPrefix, checkExistence):
59   # special case: extra configuration/environment files are provided by user
60   # Search for command-line argument(s) <optionPrefix>=file1,file2,..., filen
61   # Search for command-line argument(s) <optionPrefix>=dir1,dir2,..., dirn
62   configArgs = [ str(x) for x in args if str(x).startswith(optionPrefix) ]
63
64   args = [ x for x in args if not x.startswith(optionPrefix) ]
65   allLists = [ x.replace(optionPrefix, '') for x in configArgs ]
66
67   configFileNames = []
68   unexisting = []
69   for currentList in allLists:
70     elements = currentList.split(',')
71     for elt in elements:
72       elt = os.path.realpath(os.path.expanduser(elt))
73       if os.path.isdir(elt):
74         configFileNames += __listDirectory(elt)
75       else:
76         if checkExistence and not os.path.isfile(elt):
77           unexisting += [elt]
78         else:
79           configFileNames += [elt]
80
81   return configFileNames, args, unexisting
82 #
83
84 def getConfigFileNames(args, checkExistence=False):
85   configOptionPrefix = "--config="
86   configArgs = [ str(x) for x in args if str(x).startswith(configOptionPrefix) ]
87   if len(configArgs) == 0:
88     configFileNames, unexist = __getConfigFileNamesDefault(), []
89   else:
90     # get configuration filenames
91     configFileNames, args, unexist = __getEnvironmentFileNames(args, configOptionPrefix, checkExistence)
92
93   return configFileNames, args, unexist
94 #
95
96 def __getScriptPath(scriptName, searchPathList):
97   scriptName = os.path.expanduser(scriptName)
98   if os.path.isabs(scriptName):
99     return scriptName
100
101   if searchPathList is None or len(searchPathList) == 0:
102     return None
103
104   for path in searchPathList:
105     fullName = os.path.join(path, scriptName)
106     if os.path.isfile(fullName) or os.path.isfile(fullName+".py"):
107       return fullName
108
109   return None
110 #
111
112 class ScriptAndArgs:
113   # script: the command to be run, e.g. python <script.py>
114   # args: its input parameters
115   # out: its output parameters
116   def __init__(self, script=None, args=None, out=None):
117     self.script = script
118     self.args = args
119     self.out = out
120   #
121   def __repr__(self):
122     msg = "\n# Script: %s\n"%self.script
123     msg += "     * Input: %s\n"%self.args
124     msg += "     * Output: %s\n"%self.out
125     return msg
126   #
127 #
128 class ScriptAndArgsObjectEncoder(json.JSONEncoder):
129   def default(self, obj):
130     if isinstance(obj, ScriptAndArgs):
131       # to be easily parsed in GUI module (SalomeApp_Application)
132       # Do not export output arguments
133       return {obj.script:obj.args or []}
134     else:
135       return json.JSONEncoder.default(self, obj)
136 #
137
138 def getShortAndExtraArgs(args=None):
139   if args is None:
140     args = []
141   try:
142     pos = args.index("--") # raise a ValueError if not found
143     short_args = args[:pos]
144     extra_args = args[pos:] # include "--"
145   except ValueError:
146     short_args = args
147     extra_args = []
148     pass
149
150   return short_args, extra_args
151 #
152
153 # Return an array of ScriptAndArgs objects
154 def getScriptsAndArgs(args=None, searchPathList=None):
155   if args is None:
156     args = []
157   short_args, extra_args = getShortAndExtraArgs(args)
158   args = short_args
159
160   if searchPathList is None:
161     searchPathList = sys.path
162
163   # Syntax of args: script.py [args:a1,a2=val,an] ... script.py [args:a1,a2=val,an]
164   scriptArgs = []
165   currentKey = None
166   argsPrefix = "args:"
167   outPrefix = "out:"
168   callPython = False
169   afterArgs = False
170   currentScript = None
171
172   for i in range(len(args)):
173     elt = os.path.expanduser(args[i])
174     isDriver = (elt == "driver") # special case for YACS scheme execution
175
176     if elt.startswith(argsPrefix):
177       if not currentKey or callPython:
178         raise SalomeContextException("args list must follow corresponding script file in command line.")
179       elt = elt.replace(argsPrefix, '')
180       # Special process if some items of 'args:' list are themselves lists
181       # Note that an item can be a list, but not a list of lists...
182       # So we can have something like this:
183       # myscript.py args:['file1','file2'],val1,"done",[1,2,3],[True,False],"ok"
184       # 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'
185       # We have to split elt to obtain: ['[file1,file2]','val1','done','[1,2,3]','[True,False]','ok']
186       contains_list = re.findall('(\[[^\]]*\])', elt)
187       if contains_list:
188         extracted_args = []
189         x = elt.split(",")
190         # x is ['[file1', 'file2]', 'val1', 'done', '[1', '2', '3]', '[True', 'False]', 'ok']
191         list_begin_indices = [i for i in range(len(x)) if x[i].startswith('[')] # [0, 4, 7]
192         list_end_indices = [i for i in range(len(x)) if x[i].endswith(']')] # [1, 6, 8]
193         start = 0
194         for lbeg, lend in zip(list_begin_indices,list_end_indices): # [(0, 1), (4, 6), (7, 8)]
195           if lbeg > start:
196             extracted_args += x[start:lbeg]
197             pass
198           extracted_args += [','.join(x[lbeg:lend+1])]
199           start = lend+1
200           pass
201         if start < len(x):
202           extracted_args += x[start:len(x)]
203           pass
204         scriptArgs[len(scriptArgs)-1].args = extracted_args
205         pass
206       else: # a single split is enough
207         scriptArgs[len(scriptArgs)-1].args = [os.path.expanduser(x) for x in elt.split(",")]
208         pass
209       currentKey = None
210       callPython = False
211       afterArgs = True
212     elif elt.startswith(outPrefix):
213       if (not currentKey and not afterArgs) or callPython:
214         raise SalomeContextException("out list must follow both corresponding script file and its args in command line.")
215       elt = elt.replace(outPrefix, '')
216       scriptArgs[len(scriptArgs)-1].out = [os.path.expanduser(x) for x in elt.split(",")]
217       currentKey = None
218       callPython = False
219       afterArgs = False
220     elif elt.startswith("python"):
221       callPython = True
222       afterArgs = False
223     else:
224       file_extension = os.path.splitext(elt)[-1]
225       if not os.path.isfile(elt) and not os.path.isfile(elt+".py"):
226         eltInSearchPath = __getScriptPath(elt, searchPathList)
227         if eltInSearchPath is None or (not os.path.isfile(eltInSearchPath) and not os.path.isfile(eltInSearchPath+".py")):
228           if file_extension == ".py":
229             raise SalomeContextException("Script not found: %s"%elt)
230           scriptArgs.append(ScriptAndArgs(script=elt))
231           continue
232         elt = eltInSearchPath
233
234       if file_extension != ".hdf":
235         if file_extension == ".py" or isDriver:
236           currentScript = os.path.abspath(elt)
237         elif os.path.isfile(elt+".py"):
238           currentScript = os.path.abspath(elt+".py")
239         else:
240           currentScript = os.path.abspath(elt) # python script not necessary has .py extension
241         pass
242
243       if currentScript and callPython:
244         currentKey = "@PYTHONBIN@ "+currentScript
245         scriptArgs.append(ScriptAndArgs(script=currentKey))
246         callPython = False
247       elif currentScript:
248         script_extension = os.path.splitext(currentScript)[-1]
249         if isDriver:
250           currentKey = currentScript
251           scriptArgs.append(ScriptAndArgs(script=currentKey))
252           callPython = False
253         elif not os.access(currentScript, os.X_OK):
254           currentKey = "@PYTHONBIN@ "+currentScript
255           scriptArgs.append(ScriptAndArgs(script=currentKey))
256         else:
257           ispython = False
258           try:
259             fn = open(currentScript)
260             for i in range(10): # read only 10 first lines
261               ln = fn.readline()
262               if re.search("#!.*python"):
263                 ispython = True
264                 break
265               pass
266             fn.close()
267           except:
268             pass
269           if not ispython and script_extension == ".py":
270             currentKey = "@PYTHONBIN@ "+currentScript
271           else:
272             currentKey = currentScript
273             pass
274           scriptArgs.append(ScriptAndArgs(script=currentKey))
275       # CLOSE elif currentScript
276       afterArgs = False
277   # end for loop
278
279   if len(extra_args) > 1: # syntax: -- program [options] [arguments]
280     command = extra_args[1]
281     command_args = extra_args[2:]
282     scriptArgs.append(ScriptAndArgs(script=command, args=command_args))
283     pass
284
285   return scriptArgs
286 #
287
288 # Formatting scripts and args as a Bash-like command-line:
289 # script1.py [args] ; script2.py [args] ; ...
290 # scriptArgs is a list of ScriptAndArgs objects; their output parameters are omitted
291 def formatScriptsAndArgs(scriptArgs=None):
292     if scriptArgs is None:
293       return ""
294     commands = []
295     for sa_obj in scriptArgs:
296       cmd = sa_obj.script
297       if sa_obj.args:
298         cmd = " ".join([cmd]+sa_obj.args)
299       commands.append(cmd)
300
301     sep = " ; "
302     if sys.platform == "win32":
303       sep = " & "
304     command = sep.join(["%s"%x for x in commands])
305     return command
306 #
307
308 # Ensure OMNIORB_USER_PATH is defined. This variable refers to a folder in which
309 # SALOME will write omniOrb configuration files.
310 # If OMNIORB_USER_PATH is already set, only checks write access to associated directory ;
311 # an exception is raised if check fails. It allows users for choosing a specific folder.
312 # Else the function sets OMNIORB_USER_PATH this way:
313 # - If APPLI environment variable is set, OMNIORB_USER_PATH is set to ${APPLI}/USERS.
314 #   The function does not check USERS folder existence or write access. This folder
315 #   must exist ; this is the case if SALOME virtual application has been created using
316 #   appli_gen.py script.
317 # - Else OMNIORB_USER_PATH is set to user home directory.
318 def setOmniOrbUserPath():
319   omniorbUserPath = os.getenv("OMNIORB_USER_PATH")
320   if omniorbUserPath:
321     if not os.access(omniorbUserPath, os.W_OK):
322       raise Exception("Unable to get write access to directory: %s"%omniorbUserPath)
323     pass
324   else:
325     # Must be in /tmp (or equivalent) to handle application concurrency
326     try:
327       import tempfile
328       temp = tempfile.NamedTemporaryFile()
329       temp_dir = os.path.dirname(temp.name)
330       temp.close()
331       if not os.access(temp_dir, os.W_OK):
332         raise Exception("Unable to get write access to directory: %s"%temp_dir)
333       os.environ["OMNIORB_USER_PATH"] = temp_dir
334     except:
335       homePath = os.path.realpath(os.path.expanduser('~'))
336       #defaultOmniorbUserPath = os.path.join(homePath, ".salomeConfig/USERS")
337       defaultOmniorbUserPath = homePath
338       if os.getenv("APPLI"):
339         defaultOmniorbUserPath = os.path.join(homePath, os.getenv("APPLI"), "USERS")
340         pass
341       os.environ["OMNIORB_USER_PATH"] = defaultOmniorbUserPath
342 #
343
344 def getHostname():
345   return socket.gethostname().split('.')[0]
346 #