1 # -*- coding: utf-8 -*-
3 # Copyright (C) 2008-2024 EDF R&D
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.
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.
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
19 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 # Author: Jean-Philippe Argaud, jean-philippe.argaud@edf.fr, EDF R&D
24 Informations sur le code et la plateforme, et mise à jour des chemins.
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 :
31 print(PlatformInfo().getVersion())
32 created = PlatformInfo().getDate()
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 même récupérer d'objet :
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
44 __author__ = "Jean-Philippe ARGAUD"
55 # ==============================================================================
56 class PlatformInfo(object):
58 Rassemblement des informations sur le code et la plateforme
67 "Retourne le nom de l'application"
68 import daCore.version as dav
72 "Retourne le numéro de la version"
73 import daCore.version as dav
77 "Retourne la date de création de la version"
78 import daCore.version as dav
82 "Retourne l'année de création de la version"
83 import daCore.version as dav
86 def getSystemInformation(self, __prefix=""):
88 __msg += "\n%s%30s : %s"%(__prefix, "platform.system", platform.system())
89 __msg += "\n%s%30s : %s"%(__prefix, "sys.platform", sys.platform)
90 __msg += "\n%s%30s : %s"%(__prefix, "platform.version", platform.version())
91 __msg += "\n%s%30s : %s"%(__prefix, "platform.platform", platform.platform())
92 __msg += "\n%s%30s : %s"%(__prefix, "platform.machine", platform.machine())
93 if len(platform.processor()) > 0:
94 __msg += "\n%s%30s : %s"%(__prefix, "platform.processor", platform.processor())
96 if sys.platform.startswith('linux'):
97 if hasattr(platform, 'linux_distribution'):
98 __msg += "\n%s%30s : %s"%(__prefix,
99 "platform.linux_distribution", str(platform.linux_distribution())) # noqa: E128
100 elif hasattr(platform, 'dist'):
101 __msg += "\n%s%30s : %s"%(__prefix,
102 "platform.dist", str(platform.dist())) # noqa: E128
103 elif sys.platform.startswith('darwin'):
104 if hasattr(platform, 'mac_ver'):
105 # https://fr.wikipedia.org/wiki/MacOS
107 '0' : 'Cheetah', '1' : 'Puma', '2' : 'Jaguar', # noqa: E241,E203
108 '3' : 'Panther', '4' : 'Tiger', '5' : 'Leopard', # noqa: E241,E203
109 '6' : 'Snow Leopard', '7' : 'Lion', '8' : 'Mountain Lion', # noqa: E241,E203
110 '9' : 'Mavericks', '10': 'Yosemite', '11': 'El Capitan', # noqa: E241,E203
111 '12': 'Sierra', '13': 'High Sierra', '14': 'Mojave', # noqa: E241,E203
114 for key in __macosxv10:
115 __details = platform.mac_ver()[0].split('.')
116 if (len(__details) > 0) and (__details[1] == key):
117 __msg += "\n%s%30s : %s"%(__prefix,
118 "platform.mac_ver", str(platform.mac_ver()[0] + "(" + __macosxv10[key] + ")")) # noqa: E128
120 '11': 'Big Sur', '12': 'Monterey', '13': 'Ventura', # noqa: E241
123 for key in __macosxv11:
124 __details = platform.mac_ver()[0].split('.')
125 if (__details[0] == key):
126 __msg += "\n%s%30s : %s"%(__prefix,
127 "platform.mac_ver", str(platform.mac_ver()[0] + "(" + __macosxv11[key] + ")")) # noqa: E128
128 elif hasattr(platform, 'dist'):
129 __msg += "\n%s%30s : %s"%(__prefix, "platform.dist", str(platform.dist()))
130 elif os.name == 'nt':
131 __msg += "\n%s%30s : %s"%(__prefix, "platform.win32_ver", platform.win32_ver()[1])
134 __msg += "\n%s%30s : %s"%(__prefix, "platform.python_implementation", platform.python_implementation())
135 __msg += "\n%s%30s : %s"%(__prefix, "sys.executable", sys.executable)
136 __msg += "\n%s%30s : %s"%(__prefix, "sys.version", sys.version.replace('\n', ''))
137 __msg += "\n%s%30s : %s"%(__prefix, "sys.getfilesystemencoding", str(sys.getfilesystemencoding()))
138 if sys.version_info.major == 3 and sys.version_info.minor < 11: # Python 3.10
139 __msg += "\n%s%30s : %s"%(__prefix, "locale.getdefaultlocale", str(locale.getdefaultlocale()))
141 __msg += "\n%s%30s : %s"%(__prefix, "locale.getlocale", str(locale.getlocale()))
143 __msg += "\n%s%30s : %s"%(__prefix, "os.cpu_count", os.cpu_count())
144 if hasattr(os, 'sched_getaffinity'):
145 __msg += "\n%s%30s : %s"%(__prefix, "len(os.sched_getaffinity(0))", len(os.sched_getaffinity(0)))
147 __msg += "\n%s%30s : %s"%(__prefix, "len(os.sched_getaffinity(0))", "Unsupported on this platform")
149 __msg += "\n%s%30s : %s"%(__prefix, "platform.node", platform.node())
150 __msg += "\n%s%30s : %s"%(__prefix, "socket.getfqdn", socket.getfqdn())
151 __msg += "\n%s%30s : %s"%(__prefix, "os.path.expanduser", os.path.expanduser('~'))
154 def getApplicationInformation(self, __prefix=""):
156 __msg += "\n%s%30s : %s"%(__prefix, "ADAO version", self.getVersion())
158 __msg += "\n%s%30s : %s"%(__prefix, "Python version", self.getPythonVersion())
159 __msg += "\n%s%30s : %s"%(__prefix, "Numpy version", self.getNumpyVersion())
160 __msg += "\n%s%30s : %s"%(__prefix, "Scipy version", self.getScipyVersion())
161 __msg += "\n%s%30s : %s"%(__prefix, "NLopt version", self.getNloptVersion())
162 __msg += "\n%s%30s : %s"%(__prefix, "MatplotLib version", self.getMatplotlibVersion())
163 __msg += "\n%s%30s : %s"%(__prefix, "GnuplotPy version", self.getGnuplotVersion())
164 __msg += "\n%s%30s : %s"%(__prefix, "Sphinx version", self.getSphinxVersion())
165 __msg += "\n%s%30s : %s"%(__prefix, "Fmpy version", self.getFmpyVersion())
168 def getAllInformation(self, __prefix="", __title="Whole system information"):
171 __msg += "\n" + "=" * 80 + "\n" + __title + "\n" + "=" * 80 + "\n"
172 __msg += self.getSystemInformation(__prefix)
174 __msg += self.getApplicationInformation(__prefix)
177 def getPythonVersion(self):
178 "Retourne la version de python disponible"
179 return ".".join([str(x) for x in sys.version_info[0:3]]) # map(str,sys.version_info[0:3]))
181 def getNumpyVersion(self):
182 "Retourne la version de numpy disponible"
184 return numpy.version.version
186 def getScipyVersion(self):
187 "Retourne la version de scipy disponible"
189 __version = scipy.version.version
194 def getMatplotlibVersion(self):
195 "Retourne la version de matplotlib disponible"
197 __version = matplotlib.__version__
202 def getGnuplotVersion(self):
203 "Retourne la version de gnuplotpy disponible"
205 __version = Gnuplot.__version__
210 def getSphinxVersion(self):
211 "Retourne la version de sphinx disponible"
213 __version = sphinx.__version__
218 def getNloptVersion(self):
219 "Retourne la version de nlopt disponible"
221 __version = "%s.%s.%s"%(
222 nlopt.version_major(),
223 nlopt.version_minor(),
224 nlopt.version_bugfix(),
230 def getSdfVersion(self):
231 "Retourne la version de sdf disponible"
233 __version = sdf.__version__
238 def getFmpyVersion(self):
239 "Retourne la version de fmpy disponible"
241 __version = fmpy.__version__
246 def getCurrentMemorySize(self):
247 "Retourne la taille mémoire courante utilisée"
250 def MaximumPrecision(self):
251 "Retourne la précision maximale flottante pour Numpy"
254 numpy.array([1.,], dtype='float128')
260 def MachinePrecision(self):
261 # Alternative sans module :
265 # eps = (1.0 + eps/2) - 1.0
266 return sys.float_info.epsilon
269 import daCore.version as dav
270 return "%s %s (%s)"%(dav.name, dav.version, dav.date)
272 # ==============================================================================
273 # Tests d'importation de modules système
279 raise ImportError("Numpy is not available, despites the fact it is mandatory.")
284 import scipy.optimize
291 has_matplotlib = True
293 has_matplotlib = False
319 has_salome = bool( "SALOME_ROOT_DIR" in os.environ )
320 has_yacs = bool( "YACS_ROOT_DIR" in os.environ )
321 has_adao = bool( "ADAO_ROOT_DIR" in os.environ )
322 has_eficas = bool( "EFICAS_ROOT_DIR" in os.environ )
324 # ==============================================================================
325 def uniq( __sequence ):
327 Fonction pour rendre unique chaque élément d'une liste, en préservant l'ordre
330 return [x for x in __sequence if x not in __seen and not __seen.add(x)]
333 "Version transformée pour comparaison robuste, obtenue comme un tuple"
335 for sv in re.split("[_.+-]", __version):
336 serie.append(sv.zfill(6))
339 def isIterable( __sequence, __check = False, __header = "" ):
341 Vérification que l'argument est un itérable interne.
342 Remarque : pour permettre le test correct en MultiFonctions,
343 - Ne pas accepter comme itérable un "numpy.ndarray"
344 - Ne pas accepter comme itérable avec hasattr(__sequence, "__iter__")
346 if isinstance( __sequence, (list, tuple, map, dict) ):
348 elif type(__sequence).__name__ in ('generator', 'range'):
350 elif "_iterator" in type(__sequence).__name__:
352 elif "itertools" in str(type(__sequence)):
356 if __check and not __isOk:
357 raise TypeError("Not iterable or unkown input type%s: %s"%(__header, type(__sequence),))
360 def date2int( __date: str, __lang="FR" ):
362 Fonction de secours, conversion pure : dd/mm/yy hh:mm ---> int(yyyymmddhhmm)
364 __date = __date.strip()
365 if __date.count('/') == 2 and __date.count(':') == 0 and __date.count(' ') == 0:
366 d, m, y = __date.split("/")
367 __number = (10**4) * int(y) + (10**2) * int(m) + int(d)
368 elif __date.count('/') == 2 and __date.count(':') == 1 and __date.count(' ') > 0:
369 part1, part2 = __date.split()
370 d, m, y = part1.strip().split("/")
371 h, n = part2.strip().split(":")
372 __number = (10**8) * int(y) + (10**6) * int(m) + (10**4) * int(d) + (10**2) * int(h) + int(n)
374 raise ValueError("Cannot convert \"%s\" as a D/M/Y H:M date"%__date)
377 def vfloat(__value: numpy.ndarray):
379 Conversion en flottant d'un vecteur de taille 1 et de dimensions quelconques
381 if hasattr(__value, "size") and __value.size == 1:
382 return float(__value.flat[0])
383 elif isinstance(__value, (float, int)):
384 return float(__value)
386 raise ValueError("Error in converting multiple float values from array when waiting for only one")
388 def strvect2liststr( __strvect ):
390 Fonction de secours, conversion d'une chaîne de caractères de
391 représentation de vecteur en une liste de chaînes de caractères de
392 représentation de flottants
394 for st in ("array", "matrix", "list", "tuple", "[", "]", "(", ")"):
395 __strvect = __strvect.replace(st, "") # Rien
396 for st in (",", ";"):
397 __strvect = __strvect.replace(st, " ") # Blanc
398 return __strvect.split()
400 def strmatrix2liststr( __strvect ):
402 Fonction de secours, conversion d'une chaîne de caractères de
403 représentation de matrice en une liste de chaînes de caractères de
404 représentation de flottants
406 for st in ("array", "matrix", "list", "tuple", "[", "(", "'", '"'):
407 __strvect = __strvect.replace(st, "") # Rien
408 __strvect = __strvect.replace(",", " ") # Blanc
409 for st in ("]", ")"):
410 __strvect = __strvect.replace(st, ";") # "]" et ")" par ";"
411 __strvect = re.sub(r';\s*;', r';', __strvect)
412 __strvect = __strvect.rstrip(";") # Après ^ et avant v
413 __strmat = [__l.split() for __l in __strvect.split(";")]
416 def checkFileNameConformity( __filename, __warnInsteadOfPrint=True ):
417 if sys.platform.startswith("win") and len(__filename) > 256:
420 " For some shared or older file systems on Windows, a file " + \
421 "name longer than 256 characters can lead to access problems." + \
422 "\n The name of the file in question is the following:" + \
423 "\n %s")%(__filename,)
424 if __warnInsteadOfPrint:
425 logging.warning(__msg)
433 def checkFileNameImportability( __filename, __warnInsteadOfPrint=True ):
434 if str(__filename).count(".") > 1:
437 " The file name contains %i point(s) before the extension " + \
438 "separator, which can potentially lead to problems when " + \
439 "importing this file into Python, as it can then be recognized " + \
440 "as a sub-module (generating a \"ModuleNotFoundError\"). If it " + \
441 "is intentional, make sure that there is no module with the " + \
442 "same name as the part before the first point, and that there is " + \
443 "no \"__init__.py\" file in the same directory." + \
444 "\n The name of the file in question is the following:" + \
445 "\n %s")%(int(str(__filename).count(".") - 1), __filename)
446 if __warnInsteadOfPrint is None:
448 elif __warnInsteadOfPrint:
449 logging.warning(__msg)
457 # ==============================================================================
458 class PathManagement(object):
460 Mise à jour du path système pour les répertoires d'outils
462 __slots__ = ("__paths")
465 "Déclaration des répertoires statiques"
466 parent = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
468 self.__paths["daNumerics"] = os.path.join(parent, "daNumerics")
470 for v in self.__paths.values():
472 sys.path.insert(0, v )
474 # Conserve en unique exemplaire chaque chemin
475 sys.path = uniq( sys.path )
480 Renvoie le dictionnaire des chemins ajoutés
484 # ==============================================================================
485 class SystemUsage(object):
487 Permet de récupérer les différentes tailles mémoires du process courant
491 # Le module resource renvoie 0 pour les tailles mémoire. On utilise donc
492 # plutôt : http://code.activestate.com/recipes/286222/ et Wikipedia
494 _proc_status = '/proc/%d/status' % os.getpid()
495 _memo_status = '/proc/meminfo'
497 'o' : 1.0, # Multiples SI de l'octet # noqa: E203
498 'ko' : 1.e3, # noqa: E203
499 'Mo' : 1.e6, # noqa: E203
500 'Go' : 1.e9, # noqa: E203
501 'kio': 1024.0, # Multiples binaires de l'octet # noqa: E203
502 'Mio': 1024.0 * 1024.0, # noqa: E203
503 'Gio': 1024.0 * 1024.0 * 1024.0, # noqa: E203
504 'B' : 1.0, # Multiples binaires du byte=octet # noqa: E203
505 'kB' : 1024.0, # noqa: E203
506 'MB' : 1024.0 * 1024.0, # noqa: E203
507 'GB' : 1024.0 * 1024.0 * 1024.0, # noqa: E203
514 def _VmA(self, VmKey, unit):
515 "Lecture des paramètres mémoire de la machine"
517 t = open(self._memo_status)
521 return 0.0 # non-Linux?
522 i = v.index(VmKey) # get VmKey line e.g. 'VmRSS: 9999 kB\n ...'
523 v = v[i:].split(None, 3) # whitespace
525 return 0.0 # invalid format?
526 # convert Vm value to bytes
527 mem = float(v[1]) * self._scale[v[2]]
528 return mem / self._scale[unit]
530 def getAvailablePhysicalMemory(self, unit="o"):
531 "Renvoie la mémoire physique utilisable en octets"
532 return self._VmA('MemTotal:', unit)
534 def getAvailableSwapMemory(self, unit="o"):
535 "Renvoie la mémoire swap utilisable en octets"
536 return self._VmA('SwapTotal:', unit)
538 def getAvailableMemory(self, unit="o"):
539 "Renvoie la mémoire totale (physique+swap) utilisable en octets"
540 return self._VmA('MemTotal:', unit) + self._VmA('SwapTotal:', unit)
542 def getUsableMemory(self, unit="o"):
543 """Renvoie la mémoire utilisable en octets
544 Rq : il n'est pas sûr que ce décompte soit juste...
546 return self._VmA('MemFree:', unit) + self._VmA('SwapFree:', unit) + \
547 self._VmA('Cached:', unit) + self._VmA('SwapCached:', unit)
549 def _VmB(self, VmKey, unit):
550 "Lecture des paramètres mémoire du processus"
552 t = open(self._proc_status)
556 return 0.0 # non-Linux?
557 i = v.index(VmKey) # get VmKey line e.g. 'VmRSS: 9999 kB\n ...'
558 v = v[i:].split(None, 3) # whitespace
560 return 0.0 # invalid format?
561 # convert Vm value to bytes
562 mem = float(v[1]) * self._scale[v[2]]
563 return mem / self._scale[unit]
565 def getUsedMemory(self, unit="o"):
566 "Renvoie la mémoire résidente utilisée en octets"
567 return self._VmB('VmRSS:', unit)
569 def getVirtualMemory(self, unit="o"):
570 "Renvoie la mémoire totale utilisée en octets"
571 return self._VmB('VmSize:', unit)
573 def getUsedStacksize(self, unit="o"):
574 "Renvoie la taille du stack utilisé en octets"
575 return self._VmB('VmStk:', unit)
577 def getMaxUsedMemory(self, unit="o"):
578 "Renvoie la mémoire résidente maximale mesurée"
579 return self._VmB('VmHWM:', unit)
581 def getMaxVirtualMemory(self, unit="o"):
582 "Renvoie la mémoire totale maximale mesurée"
583 return self._VmB('VmPeak:', unit)
585 # ==============================================================================
586 # Tests d'importation de modules locaux
595 # ==============================================================================
596 if __name__ == "__main__":
597 print("\n AUTODIAGNOSTIC\n")