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