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"
56 # ==============================================================================
57 def uniq( __sequence ):
59 Fonction pour rendre unique chaque élément d'une liste, en préservant l'ordre
62 return [x for x in __sequence if x not in __seen and not __seen.add(x)]
64 class PathManagement(object):
66 Mise à jour du path système pour les répertoires d'outils
68 __slots__ = ("__paths")
71 "Déclaration des répertoires statiques"
72 parent = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
74 self.__paths["daNumerics"] = os.path.join(parent, "daNumerics")
76 for v in self.__paths.values():
78 sys.path.insert(0, v )
80 # Conserve en unique exemplaire chaque chemin
81 sys.path = uniq( sys.path )
86 Renvoie le dictionnaire des chemins ajoutés
90 # ==============================================================================
91 class PlatformInfo(object):
93 Rassemblement des informations sur le code et la plateforme
95 __slots__ = ("has_salome", "has_yacs", "has_adao", "has_eficas")
99 self.has_salome = bool( "SALOME_ROOT_DIR" in os.environ )
100 self.has_yacs = bool( "YACS_ROOT_DIR" in os.environ )
101 self.has_adao = bool( "ADAO_ROOT_DIR" in os.environ )
102 self.has_eficas = bool( "EFICAS_ROOT_DIR" in os.environ )
106 "Retourne le nom de l'application"
107 import daCore.version as dav
110 def getVersion(self):
111 "Retourne le numéro de la version"
112 import daCore.version as dav
116 "Retourne la date de création de la version"
117 import daCore.version as dav
121 "Retourne l'année de création de la version"
122 import daCore.version as dav
125 def getSystemInformation(self, __prefix=""):
127 __msg += "\n%s%30s : %s"%(__prefix, "platform.system", platform.system())
128 __msg += "\n%s%30s : %s"%(__prefix, "sys.platform", sys.platform)
129 __msg += "\n%s%30s : %s"%(__prefix, "platform.version", platform.version())
130 __msg += "\n%s%30s : %s"%(__prefix, "platform.platform", platform.platform())
131 __msg += "\n%s%30s : %s"%(__prefix, "platform.machine", platform.machine())
132 if len(platform.processor()) > 0:
133 __msg += "\n%s%30s : %s"%(__prefix, "platform.processor", platform.processor())
135 if sys.platform.startswith('linux'):
136 if hasattr(platform, 'linux_distribution'):
137 __msg += "\n%s%30s : %s"%(__prefix,
138 "platform.linux_distribution", str(platform.linux_distribution())) # noqa: E128
139 elif hasattr(platform, 'dist'):
140 __msg += "\n%s%30s : %s"%(__prefix,
141 "platform.dist", str(platform.dist())) # noqa: E128
142 elif sys.platform.startswith('darwin'):
143 if hasattr(platform, 'mac_ver'):
144 # https://fr.wikipedia.org/wiki/MacOS
146 '0' : 'Cheetah', '1' : 'Puma', '2' : 'Jaguar', # noqa: E241,E203
147 '3' : 'Panther', '4' : 'Tiger', '5' : 'Leopard', # noqa: E241,E203
148 '6' : 'Snow Leopard', '7' : 'Lion', '8' : 'Mountain Lion', # noqa: E241,E203
149 '9' : 'Mavericks', '10': 'Yosemite', '11': 'El Capitan', # noqa: E241,E203
150 '12': 'Sierra', '13': 'High Sierra', '14': 'Mojave', # noqa: E241,E203
153 for key in __macosxv10:
154 __details = platform.mac_ver()[0].split('.')
155 if (len(__details) > 0) and (__details[1] == key):
156 __msg += "\n%s%30s : %s"%(__prefix,
157 "platform.mac_ver", str(platform.mac_ver()[0] + "(" + __macosxv10[key] + ")")) # noqa: E128
159 '11': 'Big Sur', '12': 'Monterey', '13': 'Ventura', # noqa: E241
162 for key in __macosxv11:
163 __details = platform.mac_ver()[0].split('.')
164 if (__details[0] == key):
165 __msg += "\n%s%30s : %s"%(__prefix,
166 "platform.mac_ver", str(platform.mac_ver()[0] + "(" + __macosxv11[key] + ")")) # noqa: E128
167 elif hasattr(platform, 'dist'):
168 __msg += "\n%s%30s : %s"%(__prefix, "platform.dist", str(platform.dist()))
169 elif os.name == 'nt':
170 __msg += "\n%s%30s : %s"%(__prefix, "platform.win32_ver", platform.win32_ver()[1])
173 __msg += "\n%s%30s : %s"%(__prefix, "platform.python_implementation", platform.python_implementation())
174 __msg += "\n%s%30s : %s"%(__prefix, "sys.executable", sys.executable)
175 __msg += "\n%s%30s : %s"%(__prefix, "sys.version", sys.version.replace('\n', ''))
176 __msg += "\n%s%30s : %s"%(__prefix, "sys.getfilesystemencoding", str(sys.getfilesystemencoding()))
177 if sys.version_info.major == 3 and sys.version_info.minor < 11: # Python 3.10
178 __msg += "\n%s%30s : %s"%(__prefix, "locale.getdefaultlocale", str(locale.getdefaultlocale()))
180 __msg += "\n%s%30s : %s"%(__prefix, "locale.getlocale", str(locale.getlocale()))
182 __msg += "\n%s%30s : %s"%(__prefix, "os.cpu_count", os.cpu_count())
183 if hasattr(os, 'sched_getaffinity'):
184 __msg += "\n%s%30s : %s"%(__prefix, "len(os.sched_getaffinity(0))", len(os.sched_getaffinity(0)))
186 __msg += "\n%s%30s : %s"%(__prefix, "len(os.sched_getaffinity(0))", "Unsupported on this platform")
188 __msg += "\n%s%30s : %s"%(__prefix, "platform.node", platform.node())
189 __msg += "\n%s%30s : %s"%(__prefix, "socket.getfqdn", socket.getfqdn())
190 __msg += "\n%s%30s : %s"%(__prefix, "os.path.expanduser", os.path.expanduser('~'))
193 def getApplicationInformation(self, __prefix=""):
195 __msg += "\n%s%30s : %s"%(__prefix, "ADAO version", self.getVersion())
197 __msg += "\n%s%30s : %s"%(__prefix, "Python version", self.getPythonVersion())
198 __msg += "\n%s%30s : %s"%(__prefix, "Numpy version", self.getNumpyVersion())
199 __msg += "\n%s%30s : %s"%(__prefix, "Scipy version", self.getScipyVersion())
200 __msg += "\n%s%30s : %s"%(__prefix, "NLopt version", self.getNloptVersion())
201 __msg += "\n%s%30s : %s"%(__prefix, "MatplotLib version", self.getMatplotlibVersion())
202 __msg += "\n%s%30s : %s"%(__prefix, "GnuplotPy version", self.getGnuplotVersion())
204 __msg += "\n%s%30s : %s"%(__prefix, "Pandas version", self.getPandasVersion())
205 __msg += "\n%s%30s : %s"%(__prefix, "Fmpy version", self.getFmpyVersion())
206 __msg += "\n%s%30s : %s"%(__prefix, "Sphinx version", self.getSphinxVersion())
209 def getAllInformation(self, __prefix="", __title="Whole system information"):
212 __msg += "\n" + "=" * 80 + "\n" + __title + "\n" + "=" * 80 + "\n"
213 __msg += self.getSystemInformation(__prefix)
215 __msg += self.getApplicationInformation(__prefix)
218 def getPythonVersion(self):
219 "Retourne la version de python disponible"
220 return ".".join([str(x) for x in sys.version_info[0:3]]) # map(str,sys.version_info[0:3]))
222 # Tests des modules système
224 def _has_numpy(self):
226 import numpy # noqa: F401
229 raise ImportError("Numpy is not available, despites the fact it is mandatory.")
231 has_numpy = property(fget = _has_numpy)
233 def _has_scipy(self):
237 import scipy.optimize # noqa: F401
242 has_scipy = property(fget = _has_scipy)
244 def _has_matplotlib(self):
246 import matplotlib # noqa: F401
247 has_matplotlib = True
249 has_matplotlib = False
250 return has_matplotlib
251 has_matplotlib = property(fget = _has_matplotlib)
253 def _has_sphinx(self):
255 import sphinx # noqa: F401
260 has_sphinx = property(fget = _has_sphinx)
262 def _has_nlopt(self):
264 import nlopt # noqa: F401
269 has_nlopt = property(fget = _has_nlopt)
271 def _has_pandas(self):
273 import pandas # noqa: F401
278 has_pandas = property(fget = _has_pandas)
282 import sdf # noqa: F401
287 has_sdf = property(fget = _has_sdf)
291 import fmpy # noqa: F401
296 has_fmpy = property(fget = _has_fmpy)
298 def _has_buildingspy(self):
300 import buildingspy # noqa: F401
301 has_buildingspy = True
303 has_buildingspy = False
304 return has_buildingspy
305 has_buildingspy = property(fget = _has_buildingspy)
307 def _has_control(self):
309 import control # noqa: F401
314 has_control = property(fget = _has_control)
316 def _has_modelicares(self):
318 import modelicares # noqa: F401
319 has_modelicares = True
321 has_modelicares = False
322 return has_modelicares
323 has_modelicares = property(fget = _has_modelicares)
325 # Tests des modules locaux
327 def _has_gnuplot(self):
329 import Gnuplot # noqa: F401
334 has_gnuplot = property(fget = _has_gnuplot)
336 def _has_models(self):
338 import Models # noqa: F401
343 has_models = property(fget = _has_models)
345 def _has_pst4mod(self):
347 import pst4mod # noqa: F401
352 has_pst4mod = property(fget = _has_pst4mod)
356 def getNumpyVersion(self):
357 "Retourne la version de numpy disponible"
359 return numpy.version.version
361 def getScipyVersion(self):
362 "Retourne la version de scipy disponible"
365 __version = scipy.version.version
370 def getNloptVersion(self):
371 "Retourne la version de nlopt disponible"
374 __version = "%s.%s.%s"%(
375 nlopt.version_major(),
376 nlopt.version_minor(),
377 nlopt.version_bugfix(),
383 def getMatplotlibVersion(self):
384 "Retourne la version de matplotlib disponible"
385 if self.has_matplotlib:
387 __version = matplotlib.__version__
392 def getPandasVersion(self):
393 "Retourne la version de pandas disponible"
396 __version = pandas.__version__
401 def getGnuplotVersion(self):
402 "Retourne la version de gnuplotpy disponible"
405 __version = Gnuplot.__version__
410 def getFmpyVersion(self):
411 "Retourne la version de fmpy disponible"
414 __version = fmpy.__version__
419 def getSdfVersion(self):
420 "Retourne la version de sdf disponible"
423 __version = sdf.__version__
428 def getSphinxVersion(self):
429 "Retourne la version de sphinx disponible"
432 __version = sphinx.__version__
437 def getCurrentMemorySize(self):
438 "Retourne la taille mémoire courante utilisée"
441 def MaximumPrecision(self):
442 "Retourne la précision maximale flottante pour Numpy"
445 numpy.array([1.,], dtype='float128')
451 def MachinePrecision(self):
452 # Alternative sans module :
456 # eps = (1.0 + eps/2) - 1.0
457 return sys.float_info.epsilon
460 import daCore.version as dav
461 return "%s %s (%s)"%(dav.name, dav.version, dav.date)
463 # ==============================================================================
465 "Version transformée pour comparaison robuste, obtenue comme un tuple"
467 for sv in re.split("[_.+-]", __version):
468 serie.append(sv.zfill(6))
471 def isIterable( __sequence, __check = False, __header = "" ):
473 Vérification que l'argument est un itérable interne.
474 Remarque : pour permettre le test correct en MultiFonctions,
475 - Ne pas accepter comme itérable un "numpy.ndarray"
476 - Ne pas accepter comme itérable avec hasattr(__sequence, "__iter__")
478 if isinstance( __sequence, (list, tuple, map, dict) ):
480 elif type(__sequence).__name__ in ('generator', 'range'):
482 elif "_iterator" in type(__sequence).__name__:
484 elif "itertools" in str(type(__sequence)):
488 if __check and not __isOk:
489 raise TypeError("Not iterable or unkown input type%s: %s"%(__header, type(__sequence),))
492 def date2int( __date: str, __lang="FR" ):
494 Fonction de secours, conversion pure : dd/mm/yy hh:mm ---> int(yyyymmddhhmm)
496 __date = __date.strip()
497 if __date.count('/') == 2 and __date.count(':') == 0 and __date.count(' ') == 0:
498 d, m, y = __date.split("/")
499 __number = (10**4) * int(y) + (10**2) * int(m) + int(d)
500 elif __date.count('/') == 2 and __date.count(':') == 1 and __date.count(' ') > 0:
501 part1, part2 = __date.split()
502 d, m, y = part1.strip().split("/")
503 h, n = part2.strip().split(":")
504 __number = (10**8) * int(y) + (10**6) * int(m) + (10**4) * int(d) + (10**2) * int(h) + int(n)
506 raise ValueError("Cannot convert \"%s\" as a D/M/Y H:M date"%__date)
509 def vfloat(__value: numpy.ndarray):
511 Conversion en flottant d'un vecteur de taille 1 et de dimensions quelconques
513 if hasattr(__value, "size") and __value.size == 1:
514 return float(__value.flat[0])
515 elif isinstance(__value, (float, int)):
516 return float(__value)
518 raise ValueError("Error in converting multiple float values from array when waiting for only one")
520 def strvect2liststr( __strvect ):
522 Fonction de secours, conversion d'une chaîne de caractères de
523 représentation de vecteur en une liste de chaînes de caractères de
524 représentation de flottants
526 for st in ("array", "matrix", "list", "tuple", "[", "]", "(", ")"):
527 __strvect = __strvect.replace(st, "") # Rien
528 for st in (",", ";"):
529 __strvect = __strvect.replace(st, " ") # Blanc
530 return __strvect.split()
532 def strmatrix2liststr( __strvect ):
534 Fonction de secours, conversion d'une chaîne de caractères de
535 représentation de matrice en une liste de chaînes de caractères de
536 représentation de flottants
538 for st in ("array", "matrix", "list", "tuple", "[", "(", "'", '"'):
539 __strvect = __strvect.replace(st, "") # Rien
540 __strvect = __strvect.replace(",", " ") # Blanc
541 for st in ("]", ")"):
542 __strvect = __strvect.replace(st, ";") # "]" et ")" par ";"
543 __strvect = re.sub(r';\s*;', r';', __strvect)
544 __strvect = __strvect.rstrip(";") # Après ^ et avant v
545 __strmat = [__l.split() for __l in __strvect.split(";")]
548 def checkFileNameConformity( __filename, __warnInsteadOfPrint=True ):
549 if sys.platform.startswith("win") and len(__filename) > 256:
552 " For some shared or older file systems on Windows, a file " + \
553 "name longer than 256 characters can lead to access problems." + \
554 "\n The name of the file in question is the following:" + \
555 "\n %s")%(__filename,)
556 if __warnInsteadOfPrint:
557 logging.warning(__msg)
565 def checkFileNameImportability( __filename, __warnInsteadOfPrint=True ):
566 if str(__filename).count(".") > 1:
569 " The file name contains %i point(s) before the extension " + \
570 "separator, which can potentially lead to problems when " + \
571 "importing this file into Python, as it can then be recognized " + \
572 "as a sub-module (generating a \"ModuleNotFoundError\"). If it " + \
573 "is intentional, make sure that there is no module with the " + \
574 "same name as the part before the first point, and that there is " + \
575 "no \"__init__.py\" file in the same directory." + \
576 "\n The name of the file in question is the following:" + \
577 "\n %s")%(int(str(__filename).count(".") - 1), __filename)
578 if __warnInsteadOfPrint is None:
580 elif __warnInsteadOfPrint:
581 logging.warning(__msg)
589 # ==============================================================================
590 class SystemUsage(object):
592 Permet de récupérer les différentes tailles mémoires du process courant
596 # Le module resource renvoie 0 pour les tailles mémoire. On utilise donc
597 # plutôt : http://code.activestate.com/recipes/286222/ et Wikipedia
599 _proc_status = '/proc/%d/status' % os.getpid()
600 _memo_status = '/proc/meminfo'
602 'o' : 1.0, # Multiples SI de l'octet # noqa: E203
603 'ko' : 1.e3, # noqa: E203
604 'Mo' : 1.e6, # noqa: E203
605 'Go' : 1.e9, # noqa: E203
606 'kio': 1024.0, # Multiples binaires de l'octet # noqa: E203
607 'Mio': 1024.0 * 1024.0, # noqa: E203
608 'Gio': 1024.0 * 1024.0 * 1024.0, # noqa: E203
609 'B' : 1.0, # Multiples binaires du byte=octet # noqa: E203
610 'kB' : 1024.0, # noqa: E203
611 'MB' : 1024.0 * 1024.0, # noqa: E203
612 'GB' : 1024.0 * 1024.0 * 1024.0, # noqa: E203
619 def _VmA(self, VmKey, unit):
620 "Lecture des paramètres mémoire de la machine"
622 t = open(self._memo_status)
626 return 0.0 # non-Linux?
627 i = v.index(VmKey) # get VmKey line e.g. 'VmRSS: 9999 kB\n ...'
628 v = v[i:].split(None, 3) # whitespace
630 return 0.0 # invalid format?
631 # convert Vm value to bytes
632 mem = float(v[1]) * self._scale[v[2]]
633 return mem / self._scale[unit]
635 def getAvailablePhysicalMemory(self, unit="o"):
636 "Renvoie la mémoire physique utilisable en octets"
637 return self._VmA('MemTotal:', unit)
639 def getAvailableSwapMemory(self, unit="o"):
640 "Renvoie la mémoire swap utilisable en octets"
641 return self._VmA('SwapTotal:', unit)
643 def getAvailableMemory(self, unit="o"):
644 "Renvoie la mémoire totale (physique+swap) utilisable en octets"
645 return self._VmA('MemTotal:', unit) + self._VmA('SwapTotal:', unit)
647 def getUsableMemory(self, unit="o"):
648 """Renvoie la mémoire utilisable en octets
649 Rq : il n'est pas sûr que ce décompte soit juste...
651 return self._VmA('MemFree:', unit) + self._VmA('SwapFree:', unit) + \
652 self._VmA('Cached:', unit) + self._VmA('SwapCached:', unit)
654 def _VmB(self, VmKey, unit):
655 "Lecture des paramètres mémoire du processus"
657 t = open(self._proc_status)
661 return 0.0 # non-Linux?
662 i = v.index(VmKey) # get VmKey line e.g. 'VmRSS: 9999 kB\n ...'
663 v = v[i:].split(None, 3) # whitespace
665 return 0.0 # invalid format?
666 # convert Vm value to bytes
667 mem = float(v[1]) * self._scale[v[2]]
668 return mem / self._scale[unit]
670 def getUsedMemory(self, unit="o"):
671 "Renvoie la mémoire résidente utilisée en octets"
672 return self._VmB('VmRSS:', unit)
674 def getVirtualMemory(self, unit="o"):
675 "Renvoie la mémoire totale utilisée en octets"
676 return self._VmB('VmSize:', unit)
678 def getUsedStacksize(self, unit="o"):
679 "Renvoie la taille du stack utilisé en octets"
680 return self._VmB('VmStk:', unit)
682 def getMaxUsedMemory(self, unit="o"):
683 "Renvoie la mémoire résidente maximale mesurée"
684 return self._VmB('VmHWM:', unit)
686 def getMaxVirtualMemory(self, unit="o"):
687 "Renvoie la mémoire totale maximale mesurée"
688 return self._VmB('VmPeak:', unit)
690 # ==============================================================================
691 if __name__ == "__main__":
692 print("\n AUTODIAGNOSTIC\n")