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