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