Salome HOME
Code improvements, review and simplifications (2)
[modules/adao.git] / src / daComposant / daCore / PlatformInfo.py
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright (C) 2008-2022 EDF R&D
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 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 #
21 # Author: Jean-Philippe Argaud, jean-philippe.argaud@edf.fr, EDF R&D
22
23 """
24     Informations sur le code et la plateforme, et mise à jour des chemins.
25
26     La classe "PlatformInfo" permet de récupérer les informations générales sur
27     le code et la plateforme sous forme de strings, ou d'afficher directement
28     les informations disponibles par les méthodes. L'impression directe d'un
29     objet de cette classe affiche les informations minimales. Par exemple :
30         print(PlatformInfo())
31         print(PlatformInfo().getVersion())
32         created = PlatformInfo().getDate()
33
34     La classe "PathManagement" permet de mettre à jour les chemins système pour
35     ajouter les outils numériques, matrices... On l'utilise en instanciant
36     simplement cette classe, sans meme récupérer d'objet :
37         PathManagement()
38
39     La classe "SystemUsage" permet de  sous Unix les différentes tailles
40     mémoires du process courant. Ces tailles peuvent être assez variables et
41     dépendent de la fiabilité des informations du système dans le suivi des
42     process.
43 """
44 __author__ = "Jean-Philippe ARGAUD"
45 __all__ = []
46
47 import os
48 import sys
49 import platform
50 import locale
51 import logging
52 import re
53
54 # ==============================================================================
55 class PlatformInfo(object):
56     """
57     Rassemblement des informations sur le code et la plateforme
58     """
59     def __init__(self):
60         "Sans effet"
61         pass
62
63     def getName(self):
64         "Retourne le nom de l'application"
65         import daCore.version as dav
66         return dav.name
67
68     def getVersion(self):
69         "Retourne le numéro de la version"
70         import daCore.version as dav
71         return dav.version
72
73     def getDate(self):
74         "Retourne la date de création de la version"
75         import daCore.version as dav
76         return dav.date
77
78     def getYear(self):
79         "Retourne l'année de création de la version"
80         import daCore.version as dav
81         return dav.year
82
83     def getSystemInformation(self, __prefix=""):
84         __msg  = ""
85         __msg += "\n%s%30s : %s" %(__prefix,"platform.system",platform.system())
86         __msg += "\n%s%30s : %s" %(__prefix,"sys.platform",sys.platform)
87         __msg += "\n%s%30s : %s" %(__prefix,"platform.version",platform.version())
88         __msg += "\n%s%30s : %s" %(__prefix,"platform.platform",platform.platform())
89         __msg += "\n%s%30s : %s" %(__prefix,"platform.machine",platform.machine())
90         if len(platform.processor())>0:
91             __msg += "\n%s%30s : %s" %(__prefix,"platform.processor",platform.processor())
92         #
93         if sys.platform.startswith('linux'):
94             if hasattr(platform, 'linux_distribution'):
95                 __msg += "\n%s%30s : %s" %(__prefix,
96                     "platform.linux_distribution",str(platform.linux_distribution()))
97             elif hasattr(platform, 'dist'):
98                 __msg += "\n%s%30s : %s" %(__prefix,"platform.dist",str(platform.dist()))
99         elif sys.platform.startswith('darwin'):
100             if hasattr(platform, 'mac_ver'):
101                 __macosxv = {
102                      '0': 'Cheetah',       '1': 'Puma',         '2': 'Jaguar',
103                      '3': 'Panther',       '4': 'Tiger',        '5': 'Leopard',
104                      '6': 'Snow Leopard',  '7': 'Lion',         '8': 'Mountain Lion',
105                      '9': 'Mavericks',    '10': 'Yosemite',    '11': 'El Capitan',
106                     '12': 'Sierra',       '13': 'High Sierra', '14': 'Mojave',
107                     '15': 'Catalina',     '16': 'Big Sur',     '17': 'Monterey',
108                     }
109                 for key in __macosxv:
110                     if (platform.mac_ver()[0].split('.')[1] == key):
111                         __msg += "\n%s%30s : %s" %(__prefix,
112                             "platform.mac_ver",str(platform.mac_ver()[0]+"(" + __macosxv[key]+")"))
113             elif hasattr(platform, 'dist'):
114                 __msg += "\n%s%30s : %s" %(__prefix,"platform.dist",str(platform.dist()))
115         elif os.name == 'nt':
116             __msg += "\n%s%30s : %s" %(__prefix,"platform.win32_ver",platform.win32_ver()[1])
117         #
118         __msg += "\n"
119         __msg += "\n%s%30s : %s" %(__prefix,"platform.python_implementation",platform.python_implementation())
120         __msg += "\n%s%30s : %s" %(__prefix,"sys.executable",sys.executable)
121         __msg += "\n%s%30s : %s" %(__prefix,"sys.version",sys.version.replace('\n',''))
122         __msg += "\n%s%30s : %s" %(__prefix,"sys.getfilesystemencoding",str(sys.getfilesystemencoding()))
123         __msg += "\n%s%30s : %s" %(__prefix,"locale.getdefaultlocale",str(locale.getdefaultlocale()))
124         __msg += "\n"
125         __msg += "\n%s%30s : %s" %(__prefix,"os.cpu_count",os.cpu_count())
126         if hasattr(os, 'sched_getaffinity'):
127             __msg += "\n%s%30s : %s" %(__prefix,"len(os.sched_getaffinity(0))",len(os.sched_getaffinity(0)))
128         else:
129             __msg += "\n%s%30s : %s" %(__prefix,"len(os.sched_getaffinity(0))","Unsupported on this platform")
130         __msg += "\n"
131         __msg += "\n%s%30s : %s" %(__prefix,"platform.node",platform.node())
132         __msg += "\n%s%30s : %s" %(__prefix,"os.path.expanduser",os.path.expanduser('~'))
133         return __msg
134
135     def getPythonVersion(self):
136         "Retourne la version de python disponible"
137         return ".".join([str(x) for x in sys.version_info[0:3]]) # map(str,sys.version_info[0:3]))
138
139     def getNumpyVersion(self):
140         "Retourne la version de numpy disponible"
141         import numpy.version
142         return numpy.version.version
143
144     def getScipyVersion(self):
145         "Retourne la version de scipy disponible"
146         if has_scipy:
147             __version = scipy.version.version
148         else:
149             __version = "0.0.0"
150         return __version
151
152     def getMatplotlibVersion(self):
153         "Retourne la version de matplotlib disponible"
154         if has_matplotlib:
155             __version = matplotlib.__version__
156         else:
157             __version = "0.0.0"
158         return __version
159
160     def getGnuplotVersion(self):
161         "Retourne la version de gnuplotpy disponible"
162         if has_gnuplot:
163             __version = Gnuplot.__version__
164         else:
165             __version = "0.0"
166         return __version
167
168     def getSphinxVersion(self):
169         "Retourne la version de sphinx disponible"
170         if has_sphinx:
171             __version = sphinx.__version__
172         else:
173             __version = "0.0.0"
174         return __version
175
176     def getNloptVersion(self):
177         "Retourne la version de nlopt disponible"
178         if has_nlopt:
179             __version = "%s.%s.%s"%(
180                 nlopt.version_major(),
181                 nlopt.version_minor(),
182                 nlopt.version_bugfix(),
183                 )
184         else:
185             __version = "0.0.0"
186         return __version
187
188     def getCurrentMemorySize(self):
189         "Retourne la taille mémoire courante utilisée"
190         return 1
191
192     def MaximumPrecision(self):
193         "Retourne la precision maximale flottante pour Numpy"
194         import numpy
195         try:
196             numpy.array([1.,], dtype='float128')
197             mfp = 'float128'
198         except Exception:
199             mfp = 'float64'
200         return mfp
201
202     def MachinePrecision(self):
203         # Alternative sans module :
204         # eps = 2.38
205         # while eps > 0:
206         #     old_eps = eps
207         #     eps = (1.0 + eps/2) - 1.0
208         return sys.float_info.epsilon
209
210     def __str__(self):
211         import daCore.version as dav
212         return "%s %s (%s)"%(dav.name,dav.version,dav.date)
213
214 # ==============================================================================
215 try:
216     import scipy
217     import scipy.version
218     import scipy.optimize
219     has_scipy = True
220 except ImportError:
221     has_scipy = False
222
223 try:
224     import matplotlib
225     has_matplotlib = True
226 except ImportError:
227     has_matplotlib = False
228
229 try:
230     import Gnuplot
231     has_gnuplot = True
232 except ImportError:
233     has_gnuplot = False
234
235 try:
236     import sphinx
237     has_sphinx = True
238 except ImportError:
239     has_sphinx = False
240
241 try:
242     import nlopt
243     has_nlopt = True
244 except ImportError:
245     has_nlopt = False
246
247 try:
248     import sdf
249     has_sdf = True
250 except ImportError:
251     has_sdf = False
252
253 has_salome = bool( "ROOT_SALOME"   in os.environ )
254 has_yacs   = bool( "YACS_ROOT_DIR" in os.environ )
255 has_adao   = bool( "ADAO_ROOT_DIR" in os.environ )
256 has_eficas = bool( "EFICAS_ROOT_DIR" in os.environ )
257
258 # ==============================================================================
259 def uniq( __sequence ):
260     """
261     Fonction pour rendre unique chaque élément d'une liste, en préservant l'ordre
262     """
263     __seen = set()
264     return [x for x in __sequence if x not in __seen and not __seen.add(x)]
265
266 def isIterable( __sequence, __check = False, __header = "" ):
267     """
268     Vérification que l'argument est un itérable interne.
269     Remarque : pour permettre le test correct en MultiFonctions,
270     - Ne pas accepter comme itérable un "numpy.ndarray"
271     - Ne pas accepter comme itérable avec hasattr(__sequence, "__iter__")
272     """
273     if  isinstance( __sequence, (list, tuple, map, dict) ):
274         __isOk = True
275     elif type(__sequence).__name__ in ('generator','range'):
276         __isOk = True
277     elif "_iterator" in type(__sequence).__name__:
278         __isOk = True
279     else:
280         __isOk = False
281     if __check and not __isOk:
282         raise TypeError("Not iterable or unkown input type%s: %s"%(__header, type(__sequence),))
283     return __isOk
284
285 def date2int( __date, __lang="FR" ):
286     """
287     Fonction de secours, conversion pure : dd/mm/yy hh:mm ---> int(yyyymmddhhmm)
288     """
289     __date = __date.strip()
290     if __date.count('/') == 2 and __date.count(':') == 0 and __date.count(' ') == 0:
291         d,m,y = __date.split("/")
292         __number = (10**4)*int(y)+(10**2)*int(m)+int(d)
293     elif __date.count('/') == 2 and __date.count(':') == 1 and __date.count(' ') > 0:
294         part1, part2 = __date.split()
295         d,m,y = part1.strip().split("/")
296         h,n   = part2.strip().split(":")
297         __number = (10**8)*int(y)+(10**6)*int(m)+(10**4)*int(d)+(10**2)*int(h)+int(n)
298     else:
299         raise ValueError("Cannot convert \"%s\" as a D/M/Y H:M date"%d)
300     return __number
301
302 def strvect2liststr( __strvect ):
303     """
304     Fonction de secours, conversion d'une chaîne de caractères de
305     représentation de vecteur en une liste de chaînes de caractères de
306     représentation de flottants
307     """
308     for s in ("array", "matrix", "list", "tuple", "[", "]", "(", ")"):
309         __strvect = __strvect.replace(s,"")  # Rien
310     for s in (",", ";"):
311         __strvect = __strvect.replace(s," ") # Blanc
312     return __strvect.split()
313
314 def strmatrix2liststr( __strvect ):
315     """
316     Fonction de secours, conversion d'une chaîne de caractères de
317     représentation de matrice en une liste de chaînes de caractères de
318     représentation de flottants
319     """
320     for s in ("array", "matrix", "list", "tuple", "[", "(", "'", '"'):
321         __strvect = __strvect.replace(s,"")  # Rien
322     __strvect = __strvect.replace(","," ") # Blanc
323     for s in ("]", ")"):
324         __strvect = __strvect.replace(s,";") # "]" et ")" par ";"
325     __strvect = re.sub(';\s*;',';',__strvect)
326     __strvect = __strvect.rstrip(";") # Après ^ et avant v
327     __strmat = [l.split() for l in __strvect.split(";")]
328     return __strmat
329
330 def checkFileNameConformity( __filename, __warnInsteadOfPrint=True ):
331     if sys.platform.startswith("win") and len(__filename) > 256:
332         __conform = False
333         __msg = (" For some shared or older file systems on Windows, a file "+\
334             "name longer than 256 characters can lead to access problems."+\
335             "\n  The name of the file in question is the following:"+\
336             "\n  %s")%(__filename,)
337         if __warnInsteadOfPrint: logging.warning(__msg)
338         else:                    print(__msg)
339     else:
340         __conform = True
341     #
342     return __conform
343
344 def checkFileNameImportability( __filename, __warnInsteadOfPrint=True ):
345     if str(__filename).count(".") > 1:
346         __conform = False
347         __msg = (" The file name contains %i point(s) before the extension "+\
348             "separator, which can potentially lead to problems when "+\
349             "importing this file into Python, as it can then be recognized "+\
350             "as a sub-module (generating a \"ModuleNotFoundError\"). If it "+\
351             "is intentional, make sure that there is no module with the "+\
352             "same name as the part before the first point, and that there is "+\
353             "no \"__init__.py\" file in the same directory."+\
354             "\n  The name of the file in question is the following:"+\
355             "\n  %s")%(int(str(__filename).count(".")-1), __filename)
356         if __warnInsteadOfPrint is None: pass
357         elif __warnInsteadOfPrint:       logging.warning(__msg)
358         else:                            print(__msg)
359     else:
360         __conform = True
361     #
362     return __conform
363
364 # ==============================================================================
365 class PathManagement(object):
366     """
367     Mise à jour du path système pour les répertoires d'outils
368     """
369     def __init__(self):
370         "Déclaration des répertoires statiques"
371         parent = os.path.abspath(os.path.join(os.path.dirname(__file__),".."))
372         self.__paths = {}
373         self.__paths["daNumerics"]  = os.path.join(parent,"daNumerics")
374         #
375         for v in self.__paths.values():
376             sys.path.insert(0, v )
377         #
378         # Conserve en unique exemplaire chaque chemin
379         sys.path = uniq( sys.path )
380         del parent
381
382     def getpaths(self):
383         """
384         Renvoie le dictionnaire des chemins ajoutés
385         """
386         return self.__paths
387
388 # ==============================================================================
389 class SystemUsage(object):
390     """
391     Permet de récupérer les différentes tailles mémoires du process courant
392     """
393     #
394     # Le module resource renvoie 0 pour les tailles mémoire. On utilise donc
395     # plutôt : http://code.activestate.com/recipes/286222/ et Wikipedia
396     #
397     _proc_status = '/proc/%d/status' % os.getpid()
398     _memo_status = '/proc/meminfo'
399     _scale = {
400         'o'  : 1.0,     # Multiples SI de l'octet
401         'ko' : 1.e3,
402         'Mo' : 1.e6,
403         'Go' : 1.e9,
404         'kio': 1024.0,  # Multiples binaires de l'octet
405         'Mio': 1024.0*1024.0,
406         'Gio': 1024.0*1024.0*1024.0,
407         'B':     1.0,   # Multiples binaires du byte=octet
408         'kB' : 1024.0,
409         'MB' : 1024.0*1024.0,
410         'GB' : 1024.0*1024.0*1024.0,
411         }
412     #
413     def __init__(self):
414         "Sans effet"
415         pass
416     #
417     def _VmA(self, VmKey, unit):
418         "Lecture des paramètres mémoire de la machine"
419         try:
420             t = open(self._memo_status)
421             v = t.read()
422             t.close()
423         except IOError:
424             return 0.0           # non-Linux?
425         i = v.index(VmKey)       # get VmKey line e.g. 'VmRSS:  9999  kB\n ...'
426         v = v[i:].split(None, 3) # whitespace
427         if len(v) < 3:
428             return 0.0           # invalid format?
429         # convert Vm value to bytes
430         mem = float(v[1]) * self._scale[v[2]]
431         return mem / self._scale[unit]
432     #
433     def getAvailablePhysicalMemory(self, unit="o"):
434         "Renvoie la mémoire physique utilisable en octets"
435         return self._VmA('MemTotal:', unit)
436     #
437     def getAvailableSwapMemory(self, unit="o"):
438         "Renvoie la mémoire swap utilisable en octets"
439         return self._VmA('SwapTotal:', unit)
440     #
441     def getAvailableMemory(self, unit="o"):
442         "Renvoie la mémoire totale (physique+swap) utilisable en octets"
443         return self._VmA('MemTotal:', unit) + self._VmA('SwapTotal:', unit)
444     #
445     def getUsableMemory(self, unit="o"):
446         """Renvoie la mémoire utilisable en octets
447         Rq : il n'est pas sûr que ce décompte soit juste...
448         """
449         return self._VmA('MemFree:', unit) + self._VmA('SwapFree:', unit) + \
450                self._VmA('Cached:', unit) + self._VmA('SwapCached:', unit)
451     #
452     def _VmB(self, VmKey, unit):
453         "Lecture des paramètres mémoire du processus"
454         try:
455             t = open(self._proc_status)
456             v = t.read()
457             t.close()
458         except IOError:
459             return 0.0           # non-Linux?
460         i = v.index(VmKey)       # get VmKey line e.g. 'VmRSS:  9999  kB\n ...'
461         v = v[i:].split(None, 3) # whitespace
462         if len(v) < 3:
463             return 0.0           # invalid format?
464         # convert Vm value to bytes
465         mem = float(v[1]) * self._scale[v[2]]
466         return mem / self._scale[unit]
467     #
468     def getUsedMemory(self, unit="o"):
469         "Renvoie la mémoire résidente utilisée en octets"
470         return self._VmB('VmRSS:', unit)
471     #
472     def getVirtualMemory(self, unit="o"):
473         "Renvoie la mémoire totale utilisée en octets"
474         return self._VmB('VmSize:', unit)
475     #
476     def getUsedStacksize(self, unit="o"):
477         "Renvoie la taille du stack utilisé en octets"
478         return self._VmB('VmStk:', unit)
479     #
480     def getMaxUsedMemory(self, unit="o"):
481         "Renvoie la mémoire résidente maximale mesurée"
482         return self._VmB('VmHWM:', unit)
483     #
484     def getMaxVirtualMemory(self, unit="o"):
485         "Renvoie la mémoire totale maximale mesurée"
486         return self._VmB('VmPeak:', unit)
487
488 # ==============================================================================
489 if __name__ == "__main__":
490     print('\n AUTODIAGNOSTIC\n')