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