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