1 # -*- coding: utf-8 -*-
3 # Copyright (C) 2008-2019 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 Définit des outils de persistence et d'enregistrement de séries de valeurs
25 pour analyse ultérieure ou utilisation de calcul.
27 __author__ = "Jean-Philippe ARGAUD"
30 import os, sys, numpy, copy
33 from daCore.PlatformInfo import PathManagement ; PathManagement()
34 from daCore.PlatformInfo import has_gnuplot
38 if sys.version_info.major < 3:
41 import cPickle as pickle
46 # ==============================================================================
47 class Persistence(object):
49 Classe générale de persistence définissant les accesseurs nécessaires
52 def __init__(self, name="", unit="", basetype=str):
56 basetype : type de base de l'objet stocké à chaque pas
58 La gestion interne des données est exclusivement basée sur les variables
59 initialisées ici (qui ne sont pas accessibles depuis l'extérieur des
60 objets comme des attributs) :
61 __basetype : le type de base de chaque valeur, sous la forme d'un type
62 permettant l'instanciation ou le casting Python
63 __values : les valeurs de stockage. Par défaut, c'est None
65 self.__name = str(name)
66 self.__unit = str(unit)
68 self.__basetype = basetype
73 self.__dynamic = False
79 self.__dataobservers = []
81 def basetype(self, basetype=None):
83 Renvoie ou met en place le type de base des objets stockés
86 return self.__basetype
88 self.__basetype = basetype
90 def store(self, value=None, **kwargs):
92 Stocke une valeur avec ses informations de filtrage.
94 if value is None: raise ValueError("Value argument required")
96 self.__values.append(copy.copy(self.__basetype(value)))
97 self.__tags.append(kwargs)
99 if self.__dynamic: self.__replots()
100 __step = len(self.__values) - 1
101 for hook, parameters, scheduler in self.__dataobservers:
102 if __step in scheduler:
103 hook( self, parameters )
105 def pop(self, item=None):
107 Retire une valeur enregistree par son index de stockage. Sans argument,
108 retire le dernier objet enregistre.
112 self.__values.pop(__index)
113 self.__tags.pop(__index)
120 Renvoie la taille sous forme numpy du dernier objet stocké. Si c'est un
121 objet numpy, renvoie le shape. Si c'est un entier, un flottant, un
122 complexe, renvoie 1. Si c'est une liste ou un dictionnaire, renvoie la
123 longueur. Par défaut, renvoie 1.
125 if len(self.__values) > 0:
126 if self.__basetype in [numpy.matrix, numpy.ndarray, numpy.array, numpy.ravel]:
127 return self.__values[-1].shape
128 elif self.__basetype in [int, float]:
130 elif self.__basetype in [list, dict]:
131 return (len(self.__values[-1]),)
135 raise ValueError("Object has no shape before its first storage")
137 # ---------------------------------------------------------
139 "x.__str__() <==> str(x)"
140 msg = " Index Value Tags\n"
141 for i,v in enumerate(self.__values):
142 msg += " i=%05i %10s %s\n"%(i,v,self.__tags[i])
146 "x.__len__() <==> len(x)"
147 return len(self.__values)
149 def __getitem__(self, index=None ):
150 "x.__getitem__(y) <==> x[y]"
151 return copy.copy(self.__values[index])
153 def count(self, value):
154 "L.count(value) -> integer -- return number of occurrences of value"
155 return self.__values.count(value)
157 def index(self, value, start=0, stop=None):
158 "L.index(value, [start, [stop]]) -> integer -- return first index of value."
159 if stop is None : stop = len(self.__values)
160 return self.__values.index(value, start, stop)
162 # ---------------------------------------------------------
163 def __filteredIndexes(self, **kwargs):
164 "Function interne filtrant les index"
165 __indexOfFilteredItems = range(len(self.__tags))
166 __filteringKwTags = kwargs.keys()
167 if len(__filteringKwTags) > 0:
168 for tagKey in __filteringKwTags:
170 for i in __indexOfFilteredItems:
171 if tagKey in self.__tags[i]:
172 if self.__tags[i][tagKey] == kwargs[tagKey]:
174 elif isinstance(kwargs[tagKey],(list,tuple)) and self.__tags[i][tagKey] in kwargs[tagKey]:
176 __indexOfFilteredItems = __tmp
177 if len(__indexOfFilteredItems) == 0: break
178 return __indexOfFilteredItems
180 # ---------------------------------------------------------
181 def values(self, **kwargs):
182 "D.values() -> list of D's values"
183 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
184 return [self.__values[i] for i in __indexOfFilteredItems]
186 def keys(self, keyword=None , **kwargs):
187 "D.keys() -> list of D's keys"
188 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
190 for i in __indexOfFilteredItems:
191 if keyword in self.__tags[i]:
192 __keys.append( self.__tags[i][keyword] )
194 __keys.append( None )
197 def items(self, keyword=None , **kwargs):
198 "D.items() -> list of D's (key, value) pairs, as 2-tuples"
199 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
201 for i in __indexOfFilteredItems:
202 if keyword in self.__tags[i]:
203 __pairs.append( (self.__tags[i][keyword], self.__values[i]) )
205 __pairs.append( (None, self.__values[i]) )
209 "D.tagkeys() -> list of D's tag keys"
211 for dicotags in self.__tags:
212 __allKeys.extend( list(dicotags.keys()) )
213 __allKeys = sorted(set(__allKeys))
216 # def valueserie(self, item=None, allSteps=True, **kwargs):
217 # if item is not None:
218 # return self.__values[item]
220 # __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
221 # if not allSteps and len(__indexOfFilteredItems) > 0:
222 # return self.__values[__indexOfFilteredItems[0]]
224 # return [self.__values[i] for i in __indexOfFilteredItems]
226 def tagserie(self, item=None, withValues=False, outputTag=None, **kwargs):
227 "D.tagserie() -> list of D's tag serie"
229 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
231 __indexOfFilteredItems = [item,]
233 # Dans le cas où la sortie donne les valeurs d'un "outputTag"
234 if outputTag is not None and isinstance(outputTag,str) :
236 for index in __indexOfFilteredItems:
237 if outputTag in self.__tags[index].keys():
238 outputValues.append( self.__tags[index][outputTag] )
239 outputValues = sorted(set(outputValues))
242 # Dans le cas où la sortie donne les tags satisfaisants aux conditions
245 return [self.__tags[index] for index in __indexOfFilteredItems]
248 for index in __indexOfFilteredItems:
249 allTags.update( self.__tags[index] )
250 allKeys = sorted(allTags.keys())
253 # ---------------------------------------------------------
255 def stepnumber(self):
257 return len(self.__values)
260 def stepserie(self, **kwargs):
261 "Nombre de pas filtrés"
262 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
263 return __indexOfFilteredItems
266 def steplist(self, **kwargs):
267 "Nombre de pas filtrés"
268 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
269 return list(__indexOfFilteredItems)
271 # ---------------------------------------------------------
274 Renvoie la série, contenant à chaque pas, la valeur moyenne des données
275 au pas. Il faut que le type de base soit compatible avec les types
279 return [numpy.array(item).mean() for item in self.__values]
281 raise TypeError("Base type is incompatible with numpy")
283 def stds(self, ddof=0):
285 Renvoie la série, contenant à chaque pas, l'écart-type des données
286 au pas. Il faut que le type de base soit compatible avec les types
289 ddof : c'est le nombre de degrés de liberté pour le calcul de
290 l'écart-type, qui est dans le diviseur. Inutile avant Numpy 1.1
293 if numpy.version.version >= '1.1.0':
294 return [numpy.array(item).std(ddof=ddof) for item in self.__values]
296 return [numpy.array(item).std() for item in self.__values]
298 raise TypeError("Base type is incompatible with numpy")
302 Renvoie la série, contenant à chaque pas, la somme des données au pas.
303 Il faut que le type de base soit compatible avec les types élémentaires
307 return [numpy.array(item).sum() for item in self.__values]
309 raise TypeError("Base type is incompatible with numpy")
313 Renvoie la série, contenant à chaque pas, le minimum des données au pas.
314 Il faut que le type de base soit compatible avec les types élémentaires
318 return [numpy.array(item).min() for item in self.__values]
320 raise TypeError("Base type is incompatible with numpy")
324 Renvoie la série, contenant à chaque pas, la maximum des données au pas.
325 Il faut que le type de base soit compatible avec les types élémentaires
329 return [numpy.array(item).max() for item in self.__values]
331 raise TypeError("Base type is incompatible with numpy")
338 geometry = "600x400",
342 "Préparation des plots"
344 # Vérification de la disponibilité du module Gnuplot
346 raise ImportError("The Gnuplot module is required to plot the object.")
348 # Vérification et compléments sur les paramètres d'entrée
350 Gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -persist -geometry '+geometry
352 Gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -geometry '+geometry
355 self.__g = Gnuplot.Gnuplot() # persist=1
356 self.__g('set terminal '+Gnuplot.GnuplotOpts.default_term)
357 self.__g('set style data lines')
359 self.__g('set autoscale')
360 self.__g('set xlabel "'+str(xlabel)+'"')
361 self.__g('set ylabel "'+str(ylabel)+'"')
363 self.__ltitle = ltitle
374 geometry = "600x400",
381 Renvoie un affichage de la valeur à chaque pas, si elle est compatible
382 avec un affichage Gnuplot (donc essentiellement un vecteur). Si
383 l'argument "step" existe dans la liste des pas de stockage effectués,
384 renvoie l'affichage de la valeur stockée à ce pas "step". Si l'argument
385 "item" est correct, renvoie l'affichage de la valeur stockée au numéro
386 "item". Par défaut ou en l'absence de "step" ou "item", renvoie un
387 affichage successif de tous les pas.
390 - step : valeur du pas à afficher
391 - item : index de la valeur à afficher
392 - steps : liste unique des pas de l'axe des X, ou None si c'est
393 la numérotation par défaut
394 - title : base du titre général, qui sera automatiquement
395 complétée par la mention du pas
396 - xlabel : label de l'axe des X
397 - ylabel : label de l'axe des Y
398 - ltitle : titre associé au vecteur tracé
399 - geometry : taille en pixels de la fenêtre et position du coin haut
400 gauche, au format X11 : LxH+X+Y (défaut : 600x400)
401 - filename : base de nom de fichier Postscript pour une sauvegarde,
402 qui est automatiquement complétée par le numéro du
403 fichier calculé par incrément simple de compteur
404 - dynamic : effectue un affichage des valeurs à chaque stockage
405 (au-delà du second). La méthode "plots" permet de
406 déclarer l'affichage dynamique, et c'est la méthode
407 "__replots" qui est utilisée pour l'effectuer
408 - persist : booléen indiquant que la fenêtre affichée sera
409 conservée lors du passage au dessin suivant
410 Par défaut, persist = False
411 - pause : booléen indiquant une pause après chaque tracé, et
413 Par défaut, pause = True
415 if not self.__dynamic:
416 self.__preplots(title, xlabel, ylabel, ltitle, geometry, persist, pause )
418 self.__dynamic = True
419 if len(self.__values) == 0: return 0
421 # Tracé du ou des vecteurs demandés
423 if step is not None and step < len(self.__values):
425 elif item is not None and item < len(self.__values):
428 indexes = indexes + list(range(len(self.__values)))
431 for index in indexes:
432 self.__g('set title "'+str(title)+' (pas '+str(index)+')"')
433 if isinstance(steps,list) or isinstance(steps,numpy.ndarray):
436 Steps = list(range(len(self.__values[index])))
438 self.__g.plot( Gnuplot.Data( Steps, self.__values[index], title=ltitle ) )
442 stepfilename = "%s_%03i.ps"%(filename,i)
443 if os.path.isfile(stepfilename):
444 raise ValueError("Error: a file with this name \"%s\" already exists."%stepfilename)
445 self.__g.hardcopy(filename=stepfilename, color=1)
447 eval(input('Please press return to continue...\n'))
451 Affichage dans le cas du suivi dynamique de la variable
453 if self.__dynamic and len(self.__values) < 2: return 0
455 self.__g('set title "'+str(self.__title))
456 Steps = list(range(len(self.__values)))
457 self.__g.plot( Gnuplot.Data( Steps, self.__values, title=self.__ltitle ) )
460 eval(input('Please press return to continue...\n'))
462 # ---------------------------------------------------------
463 # On pourrait aussi utiliser d'autres attributs d'un "array" comme "tofile"
466 Renvoie la moyenne sur toutes les valeurs sans tenir compte de la
467 longueur des pas. Il faut que le type de base soit compatible avec
468 les types élémentaires numpy.
471 if self.__basetype in [int, float]:
472 return float( numpy.array(self.__values).mean() )
474 return numpy.array(self.__values).mean(axis=0)
476 raise TypeError("Base type is incompatible with numpy")
478 def std(self, ddof=0):
480 Renvoie l'écart-type de toutes les valeurs sans tenir compte de la
481 longueur des pas. Il faut que le type de base soit compatible avec
482 les types élémentaires numpy.
484 ddof : c'est le nombre de degrés de liberté pour le calcul de
485 l'écart-type, qui est dans le diviseur. Inutile avant Numpy 1.1
488 if numpy.version.version >= '1.1.0':
489 return numpy.array(self.__values).std(ddof=ddof,axis=0)
491 return numpy.array(self.__values).std(axis=0)
493 raise TypeError("Base type is incompatible with numpy")
497 Renvoie la somme de toutes les valeurs sans tenir compte de la
498 longueur des pas. Il faut que le type de base soit compatible avec
499 les types élémentaires numpy.
502 return numpy.array(self.__values).sum(axis=0)
504 raise TypeError("Base type is incompatible with numpy")
508 Renvoie le minimum de toutes les valeurs sans tenir compte de la
509 longueur des pas. Il faut que le type de base soit compatible avec
510 les types élémentaires numpy.
513 return numpy.array(self.__values).min(axis=0)
515 raise TypeError("Base type is incompatible with numpy")
519 Renvoie le maximum de toutes les valeurs sans tenir compte de la
520 longueur des pas. Il faut que le type de base soit compatible avec
521 les types élémentaires numpy.
524 return numpy.array(self.__values).max(axis=0)
526 raise TypeError("Base type is incompatible with numpy")
530 Renvoie la somme cumulée de toutes les valeurs sans tenir compte de la
531 longueur des pas. Il faut que le type de base soit compatible avec
532 les types élémentaires numpy.
535 return numpy.array(self.__values).cumsum(axis=0)
537 raise TypeError("Base type is incompatible with numpy")
545 geometry = "600x400",
551 Renvoie un affichage unique pour l'ensemble des valeurs à chaque pas, si
552 elles sont compatibles avec un affichage Gnuplot (donc essentiellement
553 un vecteur). Si l'argument "step" existe dans la liste des pas de
554 stockage effectués, renvoie l'affichage de la valeur stockée à ce pas
555 "step". Si l'argument "item" est correct, renvoie l'affichage de la
556 valeur stockée au numéro "item".
559 - steps : liste unique des pas de l'axe des X, ou None si c'est
560 la numérotation par défaut
561 - title : base du titre général, qui sera automatiquement
562 complétée par la mention du pas
563 - xlabel : label de l'axe des X
564 - ylabel : label de l'axe des Y
565 - ltitle : titre associé au vecteur tracé
566 - geometry : taille en pixels de la fenêtre et position du coin haut
567 gauche, au format X11 : LxH+X+Y (défaut : 600x400)
568 - filename : nom de fichier Postscript pour une sauvegarde
569 - persist : booléen indiquant que la fenêtre affichée sera
570 conservée lors du passage au dessin suivant
571 Par défaut, persist = False
572 - pause : booléen indiquant une pause après chaque tracé, et
574 Par défaut, pause = True
577 # Vérification de la disponibilité du module Gnuplot
579 raise ImportError("The Gnuplot module is required to plot the object.")
581 # Vérification et compléments sur les paramètres d'entrée
583 Gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -persist -geometry '+geometry
585 Gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -geometry '+geometry
588 if isinstance(steps,list) or isinstance(steps, numpy.ndarray):
591 Steps = list(range(len(self.__values[0])))
592 self.__g = Gnuplot.Gnuplot() # persist=1
593 self.__g('set terminal '+Gnuplot.GnuplotOpts.default_term)
594 self.__g('set style data lines')
596 self.__g('set autoscale')
597 self.__g('set title "'+str(title) +'"')
598 self.__g('set xlabel "'+str(xlabel)+'"')
599 self.__g('set ylabel "'+str(ylabel)+'"')
601 # Tracé du ou des vecteurs demandés
602 indexes = list(range(len(self.__values)))
603 self.__g.plot( Gnuplot.Data( Steps, self.__values[indexes.pop(0)], title=ltitle+" (pas 0)" ) )
604 for index in indexes:
605 self.__g.replot( Gnuplot.Data( Steps, self.__values[index], title=ltitle+" (pas %i)"%index ) )
608 self.__g.hardcopy(filename=filename, color=1)
610 eval(input('Please press return to continue...\n'))
612 # ---------------------------------------------------------
613 def setDataObserver(self, HookFunction = None, HookParameters = None, Scheduler = None):
615 Association à la variable d'un triplet définissant un observer
617 Le Scheduler attendu est une fréquence, une simple liste d'index ou un
621 # Vérification du Scheduler
622 # -------------------------
624 if isinstance(Scheduler,int): # Considéré comme une fréquence à partir de 0
625 Schedulers = range( 0, maxiter, int(Scheduler) )
626 elif isinstance(Scheduler,range): # Considéré comme un itérateur
627 Schedulers = Scheduler
628 elif isinstance(Scheduler,(list,tuple)): # Considéré comme des index explicites
629 Schedulers = [iLong(i) for i in Scheduler] # map( long, Scheduler )
630 else: # Dans tous les autres cas, activé par défaut
631 Schedulers = range( 0, maxiter )
633 # Stockage interne de l'observer dans la variable
634 # -----------------------------------------------
635 self.__dataobservers.append( [HookFunction, HookParameters, Schedulers] )
637 def removeDataObserver(self, HookFunction = None, AllObservers = False):
639 Suppression d'un observer nommé sur la variable.
641 On peut donner dans HookFunction la meme fonction que lors de la
642 définition, ou un simple string qui est le nom de la fonction. Si
643 AllObservers est vrai, supprime tous les observers enregistrés.
645 if hasattr(HookFunction,"func_name"):
646 name = str( HookFunction.func_name )
647 elif hasattr(HookFunction,"__name__"):
648 name = str( HookFunction.__name__ )
649 elif isinstance(HookFunction,str):
650 name = str( HookFunction )
656 for [hf, hp, hs] in self.__dataobservers:
658 if name is hf.__name__ or AllObservers: index_to_remove.append( i )
659 index_to_remove.reverse()
660 for i in index_to_remove:
661 self.__dataobservers.pop( i )
662 return len(index_to_remove)
664 def hasDataObserver(self):
665 return bool(len(self.__dataobservers) > 0)
667 # ==============================================================================
668 class SchedulerTrigger(object):
670 Classe générale d'interface de type Scheduler/Trigger
673 simplifiedCombo = None,
675 endTime = int( 1e9 ),
682 # ==============================================================================
683 class OneScalar(Persistence):
685 Classe définissant le stockage d'une valeur unique réelle (float) par pas.
687 Le type de base peut être changé par la méthode "basetype", mais il faut que
688 le nouveau type de base soit compatible avec les types par éléments de
689 numpy. On peut même utiliser cette classe pour stocker des vecteurs/listes
690 ou des matrices comme dans les classes suivantes, mais c'est déconseillé
691 pour conserver une signification claire des noms.
693 def __init__(self, name="", unit="", basetype = float):
694 Persistence.__init__(self, name, unit, basetype)
696 class OneIndex(Persistence):
698 Classe définissant le stockage d'une valeur unique entière (int) par pas.
700 def __init__(self, name="", unit="", basetype = int):
701 Persistence.__init__(self, name, unit, basetype)
703 class OneVector(Persistence):
705 Classe de stockage d'une liste de valeurs numériques homogènes par pas. Ne
706 pas utiliser cette classe pour des données hétérogènes, mais "OneList".
708 def __init__(self, name="", unit="", basetype = numpy.ravel):
709 Persistence.__init__(self, name, unit, basetype)
711 class OneMatrix(Persistence):
713 Classe de stockage d'une matrice de valeurs (numpy.matrix) par pas.
715 def __init__(self, name="", unit="", basetype = numpy.matrix):
716 Persistence.__init__(self, name, unit, basetype)
718 class OneList(Persistence):
720 Classe de stockage d'une liste de valeurs hétérogènes (list) par pas. Ne pas
721 utiliser cette classe pour des données numériques homogènes, mais
724 def __init__(self, name="", unit="", basetype = list):
725 Persistence.__init__(self, name, unit, basetype)
728 "Fonction transparente, sans effet sur son argument"
731 class OneNoType(Persistence):
733 Classe de stockage d'un objet sans modification (cast) de type. Attention,
734 selon le véritable type de l'objet stocké à chaque pas, les opérations
735 arithmétiques à base de numpy peuvent être invalides ou donner des résultats
736 inattendus. Cette classe n'est donc à utiliser qu'à bon escient
737 volontairement, et pas du tout par défaut.
739 def __init__(self, name="", unit="", basetype = NoType):
740 Persistence.__init__(self, name, unit, basetype)
742 # ==============================================================================
743 class CompositePersistence(object):
745 Structure de stockage permettant de rassembler plusieurs objets de
748 Des objets par défaut sont prévus, et des objets supplémentaires peuvent
751 def __init__(self, name="", defaults=True):
755 La gestion interne des données est exclusivement basée sur les variables
756 initialisées ici (qui ne sont pas accessibles depuis l'extérieur des
757 objets comme des attributs) :
758 __StoredObjects : objets de type persistence collectés dans cet objet
760 self.__name = str(name)
762 self.__StoredObjects = {}
764 # Definition des objets par defaut
765 # --------------------------------
767 self.__StoredObjects["Informations"] = OneNoType("Informations")
768 self.__StoredObjects["Background"] = OneVector("Background", basetype=numpy.array)
769 self.__StoredObjects["BackgroundError"] = OneMatrix("BackgroundError")
770 self.__StoredObjects["Observation"] = OneVector("Observation", basetype=numpy.array)
771 self.__StoredObjects["ObservationError"] = OneMatrix("ObservationError")
772 self.__StoredObjects["Analysis"] = OneVector("Analysis", basetype=numpy.array)
773 self.__StoredObjects["AnalysisError"] = OneMatrix("AnalysisError")
774 self.__StoredObjects["Innovation"] = OneVector("Innovation", basetype=numpy.array)
775 self.__StoredObjects["KalmanGainK"] = OneMatrix("KalmanGainK")
776 self.__StoredObjects["OperatorH"] = OneMatrix("OperatorH")
777 self.__StoredObjects["RmsOMA"] = OneScalar("RmsOMA")
778 self.__StoredObjects["RmsOMB"] = OneScalar("RmsOMB")
779 self.__StoredObjects["RmsBMA"] = OneScalar("RmsBMA")
782 def store(self, name=None, value=None, **kwargs):
784 Stockage d'une valeur "value" pour le "step" dans la variable "name".
786 if name is None: raise ValueError("Storable object name is required for storage.")
787 if name not in self.__StoredObjects.keys():
788 raise ValueError("No such name '%s' exists in storable objects."%name)
789 self.__StoredObjects[name].store( value=value, **kwargs )
791 def add_object(self, name=None, persistenceType=Persistence, basetype=None ):
793 Ajoute dans les objets stockables un nouvel objet défini par son nom, son
794 type de Persistence et son type de base à chaque pas.
796 if name is None: raise ValueError("Object name is required for adding an object.")
797 if name in self.__StoredObjects.keys():
798 raise ValueError("An object with the same name '%s' already exists in storable objects. Choose another one."%name)
800 self.__StoredObjects[name] = persistenceType( name=str(name) )
802 self.__StoredObjects[name] = persistenceType( name=str(name), basetype=basetype )
804 def get_object(self, name=None ):
806 Renvoie l'objet de type Persistence qui porte le nom demandé.
808 if name is None: raise ValueError("Object name is required for retrieving an object.")
809 if name not in self.__StoredObjects.keys():
810 raise ValueError("No such name '%s' exists in stored objects."%name)
811 return self.__StoredObjects[name]
813 def set_object(self, name=None, objet=None ):
815 Affecte directement un 'objet' qui porte le nom 'name' demandé.
816 Attention, il n'est pas effectué de vérification sur le type, qui doit
817 comporter les méthodes habituelles de Persistence pour que cela
820 if name is None: raise ValueError("Object name is required for setting an object.")
821 if name in self.__StoredObjects.keys():
822 raise ValueError("An object with the same name '%s' already exists in storable objects. Choose another one."%name)
823 self.__StoredObjects[name] = objet
825 def del_object(self, name=None ):
827 Supprime un objet de la liste des objets stockables.
829 if name is None: raise ValueError("Object name is required for retrieving an object.")
830 if name not in self.__StoredObjects.keys():
831 raise ValueError("No such name '%s' exists in stored objects."%name)
832 del self.__StoredObjects[name]
834 # ---------------------------------------------------------
835 # Méthodes d'accès de type dictionnaire
836 def __getitem__(self, name=None ):
837 "x.__getitem__(y) <==> x[y]"
838 return self.get_object( name )
840 def __setitem__(self, name=None, objet=None ):
841 "x.__setitem__(i, y) <==> x[i]=y"
842 self.set_object( name, objet )
845 "D.keys() -> list of D's keys"
846 return self.get_stored_objects(hideVoidObjects = False)
849 "D.values() -> list of D's values"
850 return self.__StoredObjects.values()
853 "D.items() -> list of D's (key, value) pairs, as 2-tuples"
854 return self.__StoredObjects.items()
856 # ---------------------------------------------------------
857 def get_stored_objects(self, hideVoidObjects = False):
858 "Renvoie la liste des objets présents"
859 objs = self.__StoredObjects.keys()
864 if len(self.__StoredObjects[k]) > 0: usedObjs.append( k )
871 # ---------------------------------------------------------
872 def save_composite(self, filename=None, mode="pickle", compress="gzip"):
874 Enregistre l'objet dans le fichier indiqué selon le "mode" demandé,
875 et renvoi le nom du fichier
878 if compress == "gzip":
879 filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl.gz"
880 elif compress == "bzip2":
881 filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl.bz2"
883 filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl"
885 filename = os.path.abspath( filename )
888 if compress == "gzip":
889 output = gzip.open( filename, 'wb')
890 elif compress == "bzip2":
891 output = bz2.BZ2File( filename, 'wb')
893 output = open( filename, 'wb')
894 pickle.dump(self, output)
897 raise ValueError("Save mode '%s' unknown. Choose another one."%mode)
901 def load_composite(self, filename=None, mode="pickle", compress="gzip"):
903 Recharge un objet composite sauvé en fichier
906 raise ValueError("A file name if requested to load a composite.")
908 filename = os.path.abspath( filename )
911 if compress == "gzip":
912 pkl_file = gzip.open( filename, 'rb')
913 elif compress == "bzip2":
914 pkl_file = bz2.BZ2File( filename, 'rb')
916 pkl_file = open(filename, 'rb')
917 output = pickle.load(pkl_file)
918 for k in output.keys():
921 raise ValueError("Load mode '%s' unknown. Choose another one."%mode)
925 # ==============================================================================
926 if __name__ == "__main__":
927 print('\n AUTODIAGNOSTIC\n')