Salome HOME
fix python3 and ElementTreePython3.py
[tools/sat.git] / src / __init__.py
1 #!/usr/bin/env python
2 #-*- coding:utf-8 -*-
3 #  Copyright (C) 2010-2013  CEA/DEN
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.
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
20 """\
21 initial imports and utilities methods for salomeTools
22 """
23
24 import os
25 import shutil
26 import errno
27 import stat
28 import fnmatch
29 import pprint as PP
30
31 from . import pyconf
32 from . import architecture
33 from . import printcolors
34 from . import options
35 from . import system
36 from . import ElementTree
37 from . import logger
38 from . import product
39 from . import environment
40 from . import fileEnviron
41 from . import compilation
42 from . import test_module
43 from . import template
44
45 import platform
46 if platform.system() == "Windows" :
47     import colorama
48     colorama.init()
49
50 OK_STATUS = "OK"
51 KO_STATUS = "KO"
52 NA_STATUS = "NA"
53 KNOWNFAILURE_STATUS = "KF"
54 TIMEOUT_STATUS = "TIMEOUT"
55
56 class SatException(Exception):
57     """rename Exception Class"""
58     pass
59
60 def ensure_path_exists(p):
61     """Create a path if not existing
62     
63     :param p str: The path.
64     """
65     if not os.path.exists(p):
66         os.makedirs(p)
67         
68 def check_config_has_application( config, details = None ):
69     """check that the config has the key APPLICATION. Else raise an exception.
70     
71     :param config class 'common.pyconf.Config': The config.
72     """
73     if 'APPLICATION' not in config:
74         message = _("An APPLICATION is required. Use 'config --list' to get the list of available applications.\n")
75         if details :
76             details.append(message)
77         raise SatException( message )
78
79 def check_config_has_profile( config, details = None ):
80     """\
81     check that the config has the key APPLICATION.profile.
82     else, raise an exception.
83     
84     :param config class 'common.pyconf.Config': The config.
85     """
86     check_config_has_application(config)
87     if 'profile' not in config.APPLICATION:
88         message = _("A profile section is required in your application.\n")
89         if details :
90             details.append(message)
91         raise SatException( message )
92
93 def config_has_application( config ):
94     return 'APPLICATION' in config
95
96 def get_cfg_param(config, param_name, default):
97     """\
98     eearch for param_name value in config.
99     if param_name is not in config 
100     then return default,
101     else return the found value
102        
103     :param config class 'common.pyconf.Config': The config.
104     :param param_name str: the name of the parameter to get the value
105     :param default str: The value to return if param_name is not in config
106     :return: see initial description of the function
107     :rtype: str
108     """
109     if param_name in config:
110         return config[param_name]
111     return default
112
113 def strSplitN(aList, nb, skip="\n     "):
114     """
115     example
116     aStr = 'this-is-a-string'
117     splitN(aStr, 2, '-')
118     split it by every 2nd '-' rather than every '-'
119     """
120     strValue = ""
121     i = 0
122     for v in aList:
123       strValue += "%15s, " % str(v)
124       i += 1
125       if i >= nb:
126         strValue += skip
127         i = 0
128     if len(aList) > nb:
129         strValue = skip + strValue
130     return strValue
131
132 def getProductNames(cfg, wildcards, logger):
133     """get products names using * or ? as wildcards like shell Linux"""
134     res = []
135     if type(wildcards) is list:
136       wilds = wildcards
137     else:
138       wilds = [wildcards]
139     notFound = {}
140     products = cfg.APPLICATION.products.keys()
141     for wild in wildcards:
142       ok = False
143       for prod in products:
144         filtered = fnmatch.filter([prod], wild)
145         # print("filtered", prod, wild, filtered)
146         if len(filtered) > 0:
147           res.append(prod)
148           ok = True
149           break
150       if not ok:
151         notFound[wild] = None
152     if len(res) == 0:
153       logger.warning("Empty list of products, from %s" % wilds)
154     if len(notFound.keys()) > 0:
155       strProd = strSplitN( sorted(products), 5)
156       logger.warning("products not found: %s\n  availables products are:\n%s" % \
157                      (sorted(notFound.keys()), strProd) )
158     return res
159
160
161 def print_info(logger, info):
162     """\
163     Prints the tuples that are in info variable in a formatted way.
164     
165     :param logger Logger: The logging instance to use for the prints.
166     :param info list: The list of tuples to display
167     """
168     # find the maximum length of the first value of the tuples in info
169     smax = max(map(lambda l: len(l[0]), info))
170     # Print each item of info with good indentation
171     for i in info:
172         sp = " " * (smax - len(i[0]))
173         printcolors.print_value(logger, sp + i[0], i[1], 2)
174     logger.write("\n", 2)
175
176 def get_base_path(config):
177     """\
178     Returns the path of the products base.
179     
180     :param config Config: The global Config instance.
181     :return: The path of the products base.
182     :rtype: str
183     """
184     if "base" not in config.LOCAL:
185         local_file_path = os.path.join(config.VARS.salometoolsway,
186                                       "data",
187                                       "local.pyconf")
188         msg = _("Please define a base path in the file %s" % local_file_path)
189         raise SatException(msg)
190         
191     base_path = os.path.abspath(config.LOCAL.base)
192     
193     return base_path
194
195 def get_launcher_name(config):
196     """\
197     Returns the name of salome launcher.
198     
199     :param config Config: The global Config instance.
200     :return: The name of salome launcher.
201     :rtype: str
202     """
203     check_config_has_application(config)
204     if 'profile' in config.APPLICATION and 'launcher_name' in config.APPLICATION.profile:
205         launcher_name = config.APPLICATION.profile.launcher_name
206     else:
207         launcher_name = 'salome'
208
209     return launcher_name
210
211 def get_log_path(config):
212     """\
213     Returns the path of the logs.
214     
215     :param config Config: The global Config instance.
216     :return: The path of the logs.
217     :rtype: str
218     """
219     if "log_dir" not in config.LOCAL:
220         local_file_path = os.path.join(config.VARS.salometoolsway,
221                                       "data",
222                                       "local.pyconf")
223         msg = _("Please define a log_dir in the file %s" % local_file_path)
224         raise SatException(msg)
225       
226     log_dir_path = os.path.abspath(config.LOCAL.log_dir)
227     
228     return log_dir_path
229
230 def get_salome_version(config):
231     import versionMinorMajorPatch as VMMP
232
233     if hasattr(config.APPLICATION, 'version_salome'):
234         version = VMMP.MinorMajorPatch(config.APPLICATION.version_salome)
235     else:
236         kernel_info = product.get_product_config(config, "KERNEL")
237         aFile = os.path.join(
238                             kernel_info.install_dir,
239                             "bin",
240                             "salome",
241                             "VERSION")
242         if not os.path.isfile(aFile):
243             return None
244         with open(aFile) as f:
245           line = f.readline()  # example: '[SALOME KERNEL] : 8.4.0'
246         version = VMMP.MinorMajorPatch(line.split(":")[1])
247
248     res = version.strCompact()
249     # print("get_salome_version %s -> %s" % (version, res))
250     return int(res) # TODO may be future avoid test(s) on integer, use MajorMinorPatch
251
252 def read_config_from_a_file(filePath):
253         try:
254             cfg_file = pyconf.Config(filePath)
255         except pyconf.ConfigError as e:
256             raise SatException(_("Error in configuration file: %(file)s\n  %(error)s") % \
257                 { 'file': filePath, 'error': str(e) })
258         return cfg_file
259
260 def get_tmp_filename(cfg, name):
261     if not os.path.exists(cfg.VARS.tmp_root):
262         os.makedirs(cfg.VARS.tmp_root)
263
264     return os.path.join(cfg.VARS.tmp_root, name)
265
266 ##
267 # Utils class to simplify path manipulations.
268 class Path:
269     def __init__(self, path):
270         self.path = str(path)
271
272     def __add__(self, other):
273         return Path(os.path.join(self.path, str(other)))
274
275     def __abs__(self):
276         return Path(os.path.abspath(self.path))
277
278     def __str__(self):
279         return self.path
280
281     def __eq__(self, other):
282         return self.path == other.path
283
284     def exists(self):
285         return self.islink() or os.path.exists(self.path)
286
287     def islink(self):
288         return os.path.islink(self.path)
289
290     def isdir(self):
291         return os.path.isdir(self.path)
292
293     def isfile(self):
294         return os.path.isfile(self.path)
295
296     def list(self):
297         return [Path(p) for p in os.listdir(self.path)]
298
299     def dir(self):
300         return Path(os.path.dirname(self.path))
301
302     def base(self):
303         return Path(os.path.basename(self.path))
304
305     def make(self, mode=None):
306         os.makedirs(self.path)        
307         if mode:
308             os.chmod(self.path, mode)
309         
310     def chmod(self, mode):
311         os.chmod(self.path, mode)
312
313     def rm(self):    
314         if self.islink():
315             os.remove(self.path)
316         else:
317             shutil.rmtree( self.path, onerror = handleRemoveReadonly )
318
319     def copy(self, path, smart=False):
320         if not isinstance(path, Path):
321             path = Path(path)
322
323         if os.path.islink(self.path):
324             return self.copylink(path)
325         elif os.path.isdir(self.path):
326             return self.copydir(path, smart)
327         else:
328             return self.copyfile(path)
329
330     def smartcopy(self, path):
331         return self.copy(path, True)
332
333     def readlink(self):
334         if self.islink():
335             return os.readlink(self.path)
336         else:
337             return False
338
339     def symlink(self, path):
340         try:
341             os.symlink(str(path), self.path)
342             return True
343         except:
344             return False
345
346     def copylink(self, path):
347         try:
348             os.symlink(os.readlink(self.path), str(path))
349             return True
350         except:
351             return False
352
353     def copydir(self, dst, smart=False):
354         try:
355             names = self.list()
356
357             if not dst.exists():
358                 dst.make()
359
360             for name in names:
361                 if name == dst:
362                     continue
363                 if smart and (str(name) in [".git", "CVS", ".svn"]):
364                     continue
365                 srcname = self + name
366                 dstname = dst + name
367                 srcname.copy(dstname, smart)
368             return True
369         except:
370             return False
371
372     def copyfile(self, path):
373         try:
374             shutil.copy2(self.path, str(path))
375             return True
376         except:
377             return False
378
379 def find_file_in_lpath(file_name, lpath, additional_dir = ""):
380     """\
381     Find in all the directories in lpath list the file that has the same name
382     as file_name. 
383     If it is found 
384     then return the full path of the file
385     else return False.
386  
387     The additional_dir (optional) is the name of the directory to add to all 
388     paths in lpath.
389     
390     :param file_name str: The file name to search
391     :param lpath List: The list of directories where to search
392     :param additional_dir str: The name of the additional directory
393     :return: the full path of the file or False if not found
394     :rtype: str
395     """
396     for directory in lpath:
397         dir_complete = os.path.join(directory, additional_dir)
398         if not os.path.isdir(directory) or not os.path.isdir(dir_complete):
399             continue
400         l_files = os.listdir(dir_complete)
401         for file_n in l_files:
402             if file_n == file_name:
403                 return os.path.join(dir_complete, file_name)
404     return False
405
406 def handleRemoveReadonly(func, path, exc):
407     excvalue = exc[1]
408     if func in (os.rmdir, os.remove) and excvalue.errno == errno.EACCES:
409         os.chmod(path, stat.S_IRWXU| stat.S_IRWXG| stat.S_IRWXO) # 0777
410         func(path)
411     else:
412         raise
413
414 def deepcopy_list(input_list):
415     """\
416     Do a deep copy of a list
417     
418     :param input_list List: The list to copy
419     :return: The copy of the list
420     :rtype: List
421     """
422     res = []
423     for elem in input_list:
424         res.append(elem)
425     return res
426
427 def remove_item_from_list(input_list, item):
428     """\
429     Remove all occurences of item from input_list
430     
431     :param input_list List: The list to modify
432     :return: The without any item
433     :rtype: List
434     """
435     res = []
436     for elem in input_list:
437         if elem == item:
438             continue
439         res.append(elem)
440     return res
441
442 def parse_date(date):
443     """\
444     Transform YYYYMMDD_hhmmss into YYYY-MM-DD hh:mm:ss.
445     
446     :param date str: The date to transform
447     :return: The date in the new format
448     :rtype: str
449     """
450     if len(date) != 15:
451         return date
452     res = "%s-%s-%s %s:%s:%s" % (date[0:4],
453                                  date[4:6],
454                                  date[6:8],
455                                  date[9:11],
456                                  date[11:13],
457                                  date[13:])
458     return res
459
460 def merge_dicts(*dict_args):
461     """\
462     Given any number of dicts, shallow copy and merge into a new dict,
463     precedence goes to key value pairs in latter dicts.
464     """
465     result = {}
466     for dictionary in dict_args:
467         result.update(dictionary)
468     return result
469
470 def replace_in_file(filein, strin, strout):
471     """Replace <strin> by <strout> in file <filein>"""
472     shutil.move(filein, filein + "_old")
473     fileout= filein
474     filein = filein + "_old"
475     fin = open(filein, "r")
476     fout = open(fileout, "w")
477     for line in fin:
478         fout.write(line.replace(strin, strout))
479
480 def get_property_in_product_cfg(product_cfg, pprty):
481     if not "properties" in product_cfg:
482         return None
483     if not pprty in product_cfg.properties:
484         return None
485     return product_cfg.properties[pprty]
486
487 def activate_mesa_property(config):
488     """Add mesa property into application properties
489     
490     :param config Config: The global configuration. It must have an application!
491     """
492     # Verify the existence of the file
493     if not 'properties' in config.APPLICATION:
494         config.APPLICATION.addMapping( 'properties', pyconf.Mapping(), None )
495     config.APPLICATION.properties.use_mesa="yes"
496