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")
75 self.__paths["pst4mod"] = os.path.join(parent, "daNumerics", "pst4mod")
77 for v in self.__paths.values():
79 sys.path.insert(0, v )
81 # Conserve en unique exemplaire chaque chemin
82 sys.path = uniq( sys.path )
87 Renvoie le dictionnaire des chemins ajoutés
91 # ==============================================================================
92 class PlatformInfo(object):
94 Rassemblement des informations sur le code et la plateforme
96 __slots__ = ("has_salome", "has_yacs", "has_adao", "has_eficas")
100 self.has_salome = bool( "SALOME_ROOT_DIR" in os.environ )
101 self.has_yacs = bool( "YACS_ROOT_DIR" in os.environ )
102 self.has_adao = bool( "ADAO_ROOT_DIR" in os.environ )
103 self.has_eficas = bool( "EFICAS_ROOT_DIR" in os.environ )
107 "Retourne le nom de l'application"
108 import daCore.version as dav
111 def getVersion(self):
112 "Retourne le numéro de la version"
113 import daCore.version as dav
117 "Retourne la date de création de la version"
118 import daCore.version as dav
122 "Retourne l'année de création de la version"
123 import daCore.version as dav
126 def getSystemInformation(self, __prefix=""):
128 __msg += "\n%s%30s : %s"%(__prefix, "platform.system", platform.system())
129 __msg += "\n%s%30s : %s"%(__prefix, "sys.platform", sys.platform)
130 __msg += "\n%s%30s : %s"%(__prefix, "platform.version", platform.version())
131 __msg += "\n%s%30s : %s"%(__prefix, "platform.platform", platform.platform())
132 __msg += "\n%s%30s : %s"%(__prefix, "platform.machine", platform.machine())
133 if len(platform.processor()) > 0:
134 __msg += "\n%s%30s : %s"%(__prefix, "platform.processor", platform.processor())
136 if sys.platform.startswith('linux'):
137 if hasattr(platform, 'linux_distribution'):
138 __msg += "\n%s%30s : %s"%(__prefix,
139 "platform.linux_distribution", str(platform.linux_distribution())) # noqa: E128
140 elif hasattr(platform, 'dist'):
141 __msg += "\n%s%30s : %s"%(__prefix,
142 "platform.dist", str(platform.dist())) # noqa: E128
143 elif sys.platform.startswith('darwin'):
144 if hasattr(platform, 'mac_ver'):
145 # https://fr.wikipedia.org/wiki/MacOS
147 '0' : 'Cheetah', '1' : 'Puma', '2' : 'Jaguar', # noqa: E241,E203
148 '3' : 'Panther', '4' : 'Tiger', '5' : 'Leopard', # noqa: E241,E203
149 '6' : 'Snow Leopard', '7' : 'Lion', '8' : 'Mountain Lion', # noqa: E241,E203
150 '9' : 'Mavericks', '10': 'Yosemite', '11': 'El Capitan', # noqa: E241,E203
151 '12': 'Sierra', '13': 'High Sierra', '14': 'Mojave', # noqa: E241,E203
154 for key in __macosxv10:
155 __details = platform.mac_ver()[0].split('.')
156 if (len(__details) > 0) and (__details[1] == key):
157 __msg += "\n%s%30s : %s"%(__prefix,
158 "platform.mac_ver", str(platform.mac_ver()[0] + "(" + __macosxv10[key] + ")")) # noqa: E128
160 '11': 'Big Sur', '12': 'Monterey', '13': 'Ventura', # noqa: E241
161 '14': 'Sonoma', '15': 'Sequoia', # noqa: E241
163 for key in __macosxv11:
164 __details = platform.mac_ver()[0].split('.')
165 if (__details[0] == key):
166 __msg += "\n%s%30s : %s"%(__prefix,
167 "platform.mac_ver", str(platform.mac_ver()[0] + "(" + __macosxv11[key] + ")")) # noqa: E128
168 elif hasattr(platform, 'dist'):
169 __msg += "\n%s%30s : %s"%(__prefix, "platform.dist", str(platform.dist()))
170 elif os.name == 'nt':
171 __msg += "\n%s%30s : %s"%(__prefix, "platform.win32_ver", platform.win32_ver()[1])
174 __msg += "\n%s%30s : %s"%(__prefix, "platform.python_implementation", platform.python_implementation())
175 __msg += "\n%s%30s : %s"%(__prefix, "sys.executable", sys.executable)
176 __msg += "\n%s%30s : %s"%(__prefix, "sys.version", sys.version.replace('\n', ''))
177 __msg += "\n%s%30s : %s"%(__prefix, "sys.getfilesystemencoding", str(sys.getfilesystemencoding()))
178 if sys.version_info.major == 3 and sys.version_info.minor < 11: # Python 3.10
179 __msg += "\n%s%30s : %s"%(__prefix, "locale.getdefaultlocale", str(locale.getdefaultlocale()))
181 __msg += "\n%s%30s : %s"%(__prefix, "locale.getlocale", str(locale.getlocale()))
183 __msg += "\n%s%30s : %s"%(__prefix, "os.cpu_count", os.cpu_count())
184 if hasattr(os, 'sched_getaffinity'):
185 __msg += "\n%s%30s : %s"%(__prefix, "len(os.sched_getaffinity(0))", len(os.sched_getaffinity(0)))
187 __msg += "\n%s%30s : %s"%(__prefix, "len(os.sched_getaffinity(0))", "Unsupported on this platform")
189 __msg += "\n%s%30s : %s"%(__prefix, "platform.node", platform.node())
190 __msg += "\n%s%30s : %s"%(__prefix, "socket.getfqdn", socket.getfqdn())
191 __msg += "\n%s%30s : %s"%(__prefix, "os.path.expanduser", os.path.expanduser('~'))
194 def getApplicationInformation(self, __prefix=""):
196 __msg += "\n%s%30s : %s"%(__prefix, "ADAO version", self.getVersion())
198 __msg += "\n%s%30s : %s"%(__prefix, "Python version", self.getPythonVersion())
199 __msg += "\n%s%30s : %s"%(__prefix, "Numpy version", self.getNumpyVersion())
200 __msg += "\n%s%30s : %s"%(__prefix, "Scipy version", self.getScipyVersion())
201 __msg += "\n%s%30s : %s"%(__prefix, "NLopt version", self.getNloptVersion())
202 __msg += "\n%s%30s : %s"%(__prefix, "MatplotLib version", self.getMatplotlibVersion())
203 __msg += "\n%s%30s : %s"%(__prefix, "GnuplotPy version", self.getGnuplotVersion())
205 __msg += "\n%s%30s : %s"%(__prefix, "Pandas version", self.getPandasVersion())
206 __msg += "\n%s%30s : %s"%(__prefix, "Fmpy version", self.getFmpyVersion())
207 __msg += "\n%s%30s : %s"%(__prefix, "Sphinx version", self.getSphinxVersion())
210 def getAllInformation(self, __prefix="", __title="Whole system information"):
213 __msg += "\n" + "=" * 80 + "\n" + __title + "\n" + "=" * 80 + "\n"
214 __msg += self.getSystemInformation(__prefix)
216 __msg += self.getApplicationInformation(__prefix)
219 def getPythonVersion(self):
220 "Retourne la version de python disponible"
221 return ".".join([str(x) for x in sys.version_info[0:3]]) # map(str,sys.version_info[0:3]))
223 # Tests des modules système
225 def _has_numpy(self):
227 import numpy # noqa: F401
230 raise ImportError("Numpy is not available, despites the fact it is mandatory.")
232 has_numpy = property(fget = _has_numpy)
234 def _has_scipy(self):
238 import scipy.optimize # noqa: F401
243 has_scipy = property(fget = _has_scipy)
245 def _has_matplotlib(self):
247 import matplotlib # noqa: F401
248 has_matplotlib = True
250 has_matplotlib = False
251 return has_matplotlib
252 has_matplotlib = property(fget = _has_matplotlib)
254 def _has_sphinx(self):
256 import sphinx # noqa: F401
261 has_sphinx = property(fget = _has_sphinx)
263 def _has_nlopt(self):
265 import nlopt # noqa: F401
270 has_nlopt = property(fget = _has_nlopt)
272 def _has_pandas(self):
274 import pandas # noqa: F401
279 has_pandas = property(fget = _has_pandas)
283 import sdf # noqa: F401
288 has_sdf = property(fget = _has_sdf)
292 import fmpy # noqa: F401
297 has_fmpy = property(fget = _has_fmpy)
299 def _has_buildingspy(self):
301 import buildingspy # noqa: F401
302 has_buildingspy = True
304 has_buildingspy = False
305 return has_buildingspy
306 has_buildingspy = property(fget = _has_buildingspy)
308 def _has_control(self):
310 import control # noqa: F401
315 has_control = property(fget = _has_control)
317 def _has_modelicares(self):
319 import modelicares # noqa: F401
320 has_modelicares = True
322 has_modelicares = False
323 return has_modelicares
324 has_modelicares = property(fget = _has_modelicares)
326 # Tests des modules locaux
328 def _has_gnuplot(self):
330 import Gnuplot # noqa: F401
335 has_gnuplot = property(fget = _has_gnuplot)
337 def _has_models(self):
339 import Models # noqa: F401
344 has_models = property(fget = _has_models)
346 def _has_pst4mod(self):
348 import pst4mod # noqa: F401
353 has_pst4mod = property(fget = _has_pst4mod)
357 def getNumpyVersion(self):
358 "Retourne la version de numpy disponible"
360 return numpy.version.version
362 def getScipyVersion(self):
363 "Retourne la version de scipy disponible"
366 __version = scipy.version.version
371 def getNloptVersion(self):
372 "Retourne la version de nlopt disponible"
375 __version = "%s.%s.%s"%(
376 nlopt.version_major(),
377 nlopt.version_minor(),
378 nlopt.version_bugfix(),
384 def getMatplotlibVersion(self):
385 "Retourne la version de matplotlib disponible"
386 if self.has_matplotlib:
388 __version = matplotlib.__version__
393 def getPandasVersion(self):
394 "Retourne la version de pandas disponible"
397 __version = pandas.__version__
402 def getGnuplotVersion(self):
403 "Retourne la version de gnuplotpy disponible"
406 __version = Gnuplot.__version__
411 def getFmpyVersion(self):
412 "Retourne la version de fmpy disponible"
415 __version = fmpy.__version__
420 def getSdfVersion(self):
421 "Retourne la version de sdf disponible"
424 __version = sdf.__version__
429 def getSphinxVersion(self):
430 "Retourne la version de sphinx disponible"
433 __version = sphinx.__version__
438 def getCurrentMemorySize(self):
439 "Retourne la taille mémoire courante utilisée"
442 def MaximumPrecision(self):
443 "Retourne la précision maximale flottante pour Numpy"
446 numpy.array([1.,], dtype='float128')
452 def MachinePrecision(self):
453 # Alternative sans module :
457 # eps = (1.0 + eps/2) - 1.0
458 return sys.float_info.epsilon
461 import daCore.version as dav
462 return "%s %s (%s)"%(dav.name, dav.version, dav.date)
464 # ==============================================================================
466 "Version transformée pour comparaison robuste, obtenue comme un tuple"
468 for sv in re.split("[_.+-]", __version):
469 serie.append(sv.zfill(6))
472 def isIterable( __sequence, __check = False, __header = "" ):
474 Vérification que l'argument est un itérable interne.
475 Remarque : pour permettre le test correct en MultiFonctions,
476 - Ne pas accepter comme itérable un "numpy.ndarray"
477 - Ne pas accepter comme itérable avec hasattr(__sequence, "__iter__")
479 if isinstance( __sequence, (list, tuple, map, dict) ):
481 elif type(__sequence).__name__ in ('generator', 'range'):
483 elif "_iterator" in type(__sequence).__name__:
485 elif "itertools" in str(type(__sequence)):
489 if __check and not __isOk:
490 raise TypeError("Not iterable or unkown input type%s: %s"%(__header, type(__sequence),))
493 def date2int( __date: str, __lang="FR" ):
495 Fonction de secours, conversion pure : dd/mm/yy hh:mm ---> int(yyyymmddhhmm)
497 __date = __date.strip()
498 if __date.count('/') == 2 and __date.count(':') == 0 and __date.count(' ') == 0:
499 d, m, y = __date.split("/")
500 __number = (10**4) * int(y) + (10**2) * int(m) + int(d)
501 elif __date.count('/') == 2 and __date.count(':') == 1 and __date.count(' ') > 0:
502 part1, part2 = __date.split()
503 d, m, y = part1.strip().split("/")
504 h, n = part2.strip().split(":")
505 __number = (10**8) * int(y) + (10**6) * int(m) + (10**4) * int(d) + (10**2) * int(h) + int(n)
507 raise ValueError("Cannot convert \"%s\" as a D/M/Y H:M date"%__date)
510 def vfloat(__value: numpy.ndarray):
512 Conversion en flottant d'un vecteur de taille 1 et de dimensions quelconques
514 if hasattr(__value, "size") and __value.size == 1:
515 return float(__value.flat[0])
516 elif isinstance(__value, (float, int)):
517 return float(__value)
519 raise ValueError("Error in converting multiple float values from array when waiting for only one")
521 def strvect2liststr( __strvect ):
523 Fonction de secours, conversion d'une chaîne de caractères de
524 représentation de vecteur en une liste de chaînes de caractères de
525 représentation de flottants
527 for st in ("array", "matrix", "list", "tuple", "[", "]", "(", ")"):
528 __strvect = __strvect.replace(st, "") # Rien
529 for st in (",", ";"):
530 __strvect = __strvect.replace(st, " ") # Blanc
531 return __strvect.split()
533 def strmatrix2liststr( __strvect ):
535 Fonction de secours, conversion d'une chaîne de caractères de
536 représentation de matrice en une liste de chaînes de caractères de
537 représentation de flottants
539 for st in ("array", "matrix", "list", "tuple", "[", "(", "'", '"'):
540 __strvect = __strvect.replace(st, "") # Rien
541 __strvect = __strvect.replace(",", " ") # Blanc
542 for st in ("]", ")"):
543 __strvect = __strvect.replace(st, ";") # "]" et ")" par ";"
544 __strvect = re.sub(r';\s*;', r';', __strvect)
545 __strvect = __strvect.rstrip(";") # Après ^ et avant v
546 __strmat = [__l.split() for __l in __strvect.split(";")]
549 def checkFileNameConformity( __filename, __warnInsteadOfPrint=True ):
550 if sys.platform.startswith("win") and len(__filename) > 256:
553 " For some shared or older file systems on Windows, a file " + \
554 "name longer than 256 characters can lead to access problems." + \
555 "\n The name of the file in question is the following:" + \
556 "\n %s")%(__filename,)
557 if __warnInsteadOfPrint:
558 logging.warning(__msg)
566 def checkFileNameImportability( __filename, __warnInsteadOfPrint=True ):
567 if str(__filename).count(".") > 1:
570 " The file name contains %i point(s) before the extension " + \
571 "separator, which can potentially lead to problems when " + \
572 "importing this file into Python, as it can then be recognized " + \
573 "as a sub-module (generating a \"ModuleNotFoundError\"). If it " + \
574 "is intentional, make sure that there is no module with the " + \
575 "same name as the part before the first point, and that there is " + \
576 "no \"__init__.py\" file in the same directory." + \
577 "\n The name of the file in question is the following:" + \
578 "\n %s")%(int(str(__filename).count(".") - 1), __filename)
579 if __warnInsteadOfPrint is None:
581 elif __warnInsteadOfPrint:
582 logging.warning(__msg)
590 # ==============================================================================
591 class SystemUsage(object):
593 Permet de récupérer les différentes tailles mémoires du process courant
597 # Le module resource renvoie 0 pour les tailles mémoire. On utilise donc
598 # plutôt : http://code.activestate.com/recipes/286222/ et Wikipedia
600 _proc_status = '/proc/%d/status' % os.getpid()
601 _memo_status = '/proc/meminfo'
603 'o' : 1.0, # Multiples SI de l'octet # noqa: E203
604 'ko' : 1.e3, # noqa: E203
605 'Mo' : 1.e6, # noqa: E203
606 'Go' : 1.e9, # noqa: E203
607 'kio': 1024.0, # Multiples binaires de l'octet # noqa: E203
608 'Mio': 1024.0 * 1024.0, # noqa: E203
609 'Gio': 1024.0 * 1024.0 * 1024.0, # noqa: E203
610 'B' : 1.0, # Multiples binaires du byte=octet # noqa: E203
611 'kB' : 1024.0, # noqa: E203
612 'MB' : 1024.0 * 1024.0, # noqa: E203
613 'GB' : 1024.0 * 1024.0 * 1024.0, # noqa: E203
620 def _VmA(self, VmKey, unit):
621 "Lecture des paramètres mémoire de la machine"
623 t = open(self._memo_status)
627 return 0.0 # non-Linux?
628 i = v.index(VmKey) # get VmKey line e.g. 'VmRSS: 9999 kB\n ...'
629 v = v[i:].split(None, 3) # whitespace
631 return 0.0 # invalid format?
632 # convert Vm value to bytes
633 mem = float(v[1]) * self._scale[v[2]]
634 return mem / self._scale[unit]
636 def getAvailablePhysicalMemory(self, unit="o"):
637 "Renvoie la mémoire physique utilisable en octets"
638 return self._VmA('MemTotal:', unit)
640 def getAvailableSwapMemory(self, unit="o"):
641 "Renvoie la mémoire swap utilisable en octets"
642 return self._VmA('SwapTotal:', unit)
644 def getAvailableMemory(self, unit="o"):
645 "Renvoie la mémoire totale (physique+swap) utilisable en octets"
646 return self._VmA('MemTotal:', unit) + self._VmA('SwapTotal:', unit)
648 def getUsableMemory(self, unit="o"):
649 """Renvoie la mémoire utilisable en octets
650 Rq : il n'est pas sûr que ce décompte soit juste...
652 return self._VmA('MemFree:', unit) + self._VmA('SwapFree:', unit) + \
653 self._VmA('Cached:', unit) + self._VmA('SwapCached:', unit)
655 def _VmB(self, VmKey, unit):
656 "Lecture des paramètres mémoire du processus"
658 t = open(self._proc_status)
662 return 0.0 # non-Linux?
663 i = v.index(VmKey) # get VmKey line e.g. 'VmRSS: 9999 kB\n ...'
664 v = v[i:].split(None, 3) # whitespace
666 return 0.0 # invalid format?
667 # convert Vm value to bytes
668 mem = float(v[1]) * self._scale[v[2]]
669 return mem / self._scale[unit]
671 def getUsedMemory(self, unit="o"):
672 "Renvoie la mémoire résidente utilisée en octets"
673 return self._VmB('VmRSS:', unit)
675 def getVirtualMemory(self, unit="o"):
676 "Renvoie la mémoire totale utilisée en octets"
677 return self._VmB('VmSize:', unit)
679 def getUsedStacksize(self, unit="o"):
680 "Renvoie la taille du stack utilisé en octets"
681 return self._VmB('VmStk:', unit)
683 def getMaxUsedMemory(self, unit="o"):
684 "Renvoie la mémoire résidente maximale mesurée"
685 return self._VmB('VmHWM:', unit)
687 def getMaxVirtualMemory(self, unit="o"):
688 "Renvoie la mémoire totale maximale mesurée"
689 return self._VmB('VmPeak:', unit)
691 # ==============================================================================
692 if __name__ == "__main__":
693 print("\n AUTODIAGNOSTIC\n")