1 # -*- coding: utf-8 -*-
3 # Copyright (C) 2008-2023 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 meme 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"
54 # ==============================================================================
55 class PlatformInfo(object):
57 Rassemblement des informations sur le code et la plateforme
66 "Retourne le nom de l'application"
67 import daCore.version as dav
71 "Retourne le numéro de la version"
72 import daCore.version as dav
76 "Retourne la date de création de la version"
77 import daCore.version as dav
81 "Retourne l'année de création de la version"
82 import daCore.version as dav
85 def getSystemInformation(self, __prefix=""):
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())
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'):
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',
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])
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()))
128 __msg += "\n%s%30s : %s" %(__prefix,"locale.getlocale",str(locale.getlocale()))
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)))
134 __msg += "\n%s%30s : %s" %(__prefix,"len(os.sched_getaffinity(0))","Unsupported on this platform")
136 __msg += "\n%s%30s : %s" %(__prefix,"platform.node",platform.node())
137 __msg += "\n%s%30s : %s" %(__prefix,"os.path.expanduser",os.path.expanduser('~'))
140 def getApplicationInformation(self, __prefix=""):
142 __msg += "\n%s%30s : %s" %(__prefix,"ADAO version",self.getVersion())
144 __msg += "\n%s%30s : %s" %(__prefix,"Python version",self.getPythonVersion())
145 __msg += "\n%s%30s : %s" %(__prefix,"Numpy version",self.getNumpyVersion())
146 __msg += "\n%s%30s : %s" %(__prefix,"Scipy version",self.getScipyVersion())
147 __msg += "\n%s%30s : %s" %(__prefix,"NLopt version",self.getNloptVersion())
148 __msg += "\n%s%30s : %s" %(__prefix,"MatplotLib version",self.getMatplotlibVersion())
149 __msg += "\n%s%30s : %s" %(__prefix,"GnuplotPy version",self.getGnuplotVersion())
150 __msg += "\n%s%30s : %s" %(__prefix,"Sphinx version",self.getSphinxVersion())
153 def getAllInformation(self, __prefix="", __title="Whole system information"):
156 __msg += "\n"+"="*80+"\n"+__title+"\n"+"="*80+"\n"
157 __msg += self.getSystemInformation(__prefix)
159 __msg += self.getApplicationInformation(__prefix)
162 def getPythonVersion(self):
163 "Retourne la version de python disponible"
164 return ".".join([str(x) for x in sys.version_info[0:3]]) # map(str,sys.version_info[0:3]))
166 def getNumpyVersion(self):
167 "Retourne la version de numpy disponible"
169 return numpy.version.version
171 def getScipyVersion(self):
172 "Retourne la version de scipy disponible"
174 __version = scipy.version.version
179 def getMatplotlibVersion(self):
180 "Retourne la version de matplotlib disponible"
182 __version = matplotlib.__version__
187 def getGnuplotVersion(self):
188 "Retourne la version de gnuplotpy disponible"
190 __version = Gnuplot.__version__
195 def getSphinxVersion(self):
196 "Retourne la version de sphinx disponible"
198 __version = sphinx.__version__
203 def getNloptVersion(self):
204 "Retourne la version de nlopt disponible"
206 __version = "%s.%s.%s"%(
207 nlopt.version_major(),
208 nlopt.version_minor(),
209 nlopt.version_bugfix(),
215 def getSdfVersion(self):
216 "Retourne la version de sdf disponible"
218 __version = sdf.__version__
223 def getCurrentMemorySize(self):
224 "Retourne la taille mémoire courante utilisée"
227 def MaximumPrecision(self):
228 "Retourne la precision maximale flottante pour Numpy"
231 numpy.array([1.,], dtype='float128')
237 def MachinePrecision(self):
238 # Alternative sans module :
242 # eps = (1.0 + eps/2) - 1.0
243 return sys.float_info.epsilon
246 import daCore.version as dav
247 return "%s %s (%s)"%(dav.name,dav.version,dav.date)
249 # ==============================================================================
254 raise ImportError("Numpy is not available, despites the fact it is mandatory.")
259 import scipy.optimize
266 has_matplotlib = True
268 has_matplotlib = False
294 has_salome = bool( "SALOME_ROOT_DIR" in os.environ )
295 has_yacs = bool( "YACS_ROOT_DIR" in os.environ )
296 has_adao = bool( "ADAO_ROOT_DIR" in os.environ )
297 has_eficas = bool( "EFICAS_ROOT_DIR" in os.environ )
299 # ==============================================================================
300 def uniq( __sequence ):
302 Fonction pour rendre unique chaque élément d'une liste, en préservant l'ordre
305 return [x for x in __sequence if x not in __seen and not __seen.add(x)]
308 "Version transformée pour comparaison robuste, obtenue comme un tuple"
310 for sv in re.split("[_.+-]", __version):
311 serie.append(sv.zfill(6))
314 def isIterable( __sequence, __check = False, __header = "" ):
316 Vérification que l'argument est un itérable interne.
317 Remarque : pour permettre le test correct en MultiFonctions,
318 - Ne pas accepter comme itérable un "numpy.ndarray"
319 - Ne pas accepter comme itérable avec hasattr(__sequence, "__iter__")
321 if isinstance( __sequence, (list, tuple, map, dict) ):
323 elif type(__sequence).__name__ in ('generator','range'):
325 elif "_iterator" in type(__sequence).__name__:
327 elif "itertools" in str(type(__sequence)):
331 if __check and not __isOk:
332 raise TypeError("Not iterable or unkown input type%s: %s"%(__header, type(__sequence),))
335 def date2int( __date, __lang="FR" ):
337 Fonction de secours, conversion pure : dd/mm/yy hh:mm ---> int(yyyymmddhhmm)
339 __date = __date.strip()
340 if __date.count('/') == 2 and __date.count(':') == 0 and __date.count(' ') == 0:
341 d,m,y = __date.split("/")
342 __number = (10**4)*int(y)+(10**2)*int(m)+int(d)
343 elif __date.count('/') == 2 and __date.count(':') == 1 and __date.count(' ') > 0:
344 part1, part2 = __date.split()
345 d,m,y = part1.strip().split("/")
346 h,n = part2.strip().split(":")
347 __number = (10**8)*int(y)+(10**6)*int(m)+(10**4)*int(d)+(10**2)*int(h)+int(n)
349 raise ValueError("Cannot convert \"%s\" as a D/M/Y H:M date"%d)
352 def strvect2liststr( __strvect ):
354 Fonction de secours, conversion d'une chaîne de caractères de
355 représentation de vecteur en une liste de chaînes de caractères de
356 représentation de flottants
358 for s in ("array", "matrix", "list", "tuple", "[", "]", "(", ")"):
359 __strvect = __strvect.replace(s,"") # Rien
361 __strvect = __strvect.replace(s," ") # Blanc
362 return __strvect.split()
364 def strmatrix2liststr( __strvect ):
366 Fonction de secours, conversion d'une chaîne de caractères de
367 représentation de matrice en une liste de chaînes de caractères de
368 représentation de flottants
370 for s in ("array", "matrix", "list", "tuple", "[", "(", "'", '"'):
371 __strvect = __strvect.replace(s,"") # Rien
372 __strvect = __strvect.replace(","," ") # Blanc
374 __strvect = __strvect.replace(s,";") # "]" et ")" par ";"
375 __strvect = re.sub(r';\s*;',r';',__strvect)
376 __strvect = __strvect.rstrip(";") # Après ^ et avant v
377 __strmat = [__l.split() for __l in __strvect.split(";")]
380 def checkFileNameConformity( __filename, __warnInsteadOfPrint=True ):
381 if sys.platform.startswith("win") and len(__filename) > 256:
383 __msg = (" For some shared or older file systems on Windows, a file "+\
384 "name longer than 256 characters can lead to access problems."+\
385 "\n The name of the file in question is the following:"+\
386 "\n %s")%(__filename,)
387 if __warnInsteadOfPrint: logging.warning(__msg)
394 def checkFileNameImportability( __filename, __warnInsteadOfPrint=True ):
395 if str(__filename).count(".") > 1:
397 __msg = (" The file name contains %i point(s) before the extension "+\
398 "separator, which can potentially lead to problems when "+\
399 "importing this file into Python, as it can then be recognized "+\
400 "as a sub-module (generating a \"ModuleNotFoundError\"). If it "+\
401 "is intentional, make sure that there is no module with the "+\
402 "same name as the part before the first point, and that there is "+\
403 "no \"__init__.py\" file in the same directory."+\
404 "\n The name of the file in question is the following:"+\
405 "\n %s")%(int(str(__filename).count(".")-1), __filename)
406 if __warnInsteadOfPrint is None: pass
407 elif __warnInsteadOfPrint: logging.warning(__msg)
414 # ==============================================================================
415 class PathManagement(object):
417 Mise à jour du path système pour les répertoires d'outils
419 __slots__ = ("__paths")
422 "Déclaration des répertoires statiques"
423 parent = os.path.abspath(os.path.join(os.path.dirname(__file__),".."))
425 self.__paths["daNumerics"] = os.path.join(parent,"daNumerics")
427 for v in self.__paths.values():
428 if os.path.isdir(v): sys.path.insert(0, v )
430 # Conserve en unique exemplaire chaque chemin
431 sys.path = uniq( sys.path )
436 Renvoie le dictionnaire des chemins ajoutés
440 # ==============================================================================
441 class SystemUsage(object):
443 Permet de récupérer les différentes tailles mémoires du process courant
447 # Le module resource renvoie 0 pour les tailles mémoire. On utilise donc
448 # plutôt : http://code.activestate.com/recipes/286222/ et Wikipedia
450 _proc_status = '/proc/%d/status' % os.getpid()
451 _memo_status = '/proc/meminfo'
453 'o' : 1.0, # Multiples SI de l'octet
457 'kio': 1024.0, # Multiples binaires de l'octet
458 'Mio': 1024.0*1024.0,
459 'Gio': 1024.0*1024.0*1024.0,
460 'B': 1.0, # Multiples binaires du byte=octet
462 'MB' : 1024.0*1024.0,
463 'GB' : 1024.0*1024.0*1024.0,
470 def _VmA(self, VmKey, unit):
471 "Lecture des paramètres mémoire de la machine"
473 t = open(self._memo_status)
477 return 0.0 # non-Linux?
478 i = v.index(VmKey) # get VmKey line e.g. 'VmRSS: 9999 kB\n ...'
479 v = v[i:].split(None, 3) # whitespace
481 return 0.0 # invalid format?
482 # convert Vm value to bytes
483 mem = float(v[1]) * self._scale[v[2]]
484 return mem / self._scale[unit]
486 def getAvailablePhysicalMemory(self, unit="o"):
487 "Renvoie la mémoire physique utilisable en octets"
488 return self._VmA('MemTotal:', unit)
490 def getAvailableSwapMemory(self, unit="o"):
491 "Renvoie la mémoire swap utilisable en octets"
492 return self._VmA('SwapTotal:', unit)
494 def getAvailableMemory(self, unit="o"):
495 "Renvoie la mémoire totale (physique+swap) utilisable en octets"
496 return self._VmA('MemTotal:', unit) + self._VmA('SwapTotal:', unit)
498 def getUsableMemory(self, unit="o"):
499 """Renvoie la mémoire utilisable en octets
500 Rq : il n'est pas sûr que ce décompte soit juste...
502 return self._VmA('MemFree:', unit) + self._VmA('SwapFree:', unit) + \
503 self._VmA('Cached:', unit) + self._VmA('SwapCached:', unit)
505 def _VmB(self, VmKey, unit):
506 "Lecture des paramètres mémoire du processus"
508 t = open(self._proc_status)
512 return 0.0 # non-Linux?
513 i = v.index(VmKey) # get VmKey line e.g. 'VmRSS: 9999 kB\n ...'
514 v = v[i:].split(None, 3) # whitespace
516 return 0.0 # invalid format?
517 # convert Vm value to bytes
518 mem = float(v[1]) * self._scale[v[2]]
519 return mem / self._scale[unit]
521 def getUsedMemory(self, unit="o"):
522 "Renvoie la mémoire résidente utilisée en octets"
523 return self._VmB('VmRSS:', unit)
525 def getVirtualMemory(self, unit="o"):
526 "Renvoie la mémoire totale utilisée en octets"
527 return self._VmB('VmSize:', unit)
529 def getUsedStacksize(self, unit="o"):
530 "Renvoie la taille du stack utilisé en octets"
531 return self._VmB('VmStk:', unit)
533 def getMaxUsedMemory(self, unit="o"):
534 "Renvoie la mémoire résidente maximale mesurée"
535 return self._VmB('VmHWM:', unit)
537 def getMaxVirtualMemory(self, unit="o"):
538 "Renvoie la mémoire totale maximale mesurée"
539 return self._VmB('VmPeak:', unit)
541 # ==============================================================================
542 if __name__ == "__main__":
543 print('\n AUTODIAGNOSTIC\n')