1 # -*- coding: utf-8 -*-
3 # Copyright (C) 2008-2022 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 persistance 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, numpy, copy, math
31 import gzip, bz2, pickle
33 from daCore.PlatformInfo import PathManagement ; PathManagement()
34 from daCore.PlatformInfo import has_gnuplot, PlatformInfo
35 mfp = PlatformInfo().MaximumPrecision()
39 # ==============================================================================
40 class Persistence(object):
42 Classe générale de persistance définissant les accesseurs nécessaires
45 def __init__(self, name="", unit="", basetype=str):
49 basetype : type de base de l'objet stocké à chaque pas
51 La gestion interne des données est exclusivement basée sur les variables
52 initialisées ici (qui ne sont pas accessibles depuis l'extérieur des
53 objets comme des attributs) :
54 __basetype : le type de base de chaque valeur, sous la forme d'un type
55 permettant l'instanciation ou le casting Python
56 __values : les valeurs de stockage. Par défaut, c'est None
58 self.__name = str(name)
59 self.__unit = str(unit)
61 self.__basetype = basetype
66 self.__dynamic = False
72 self.__dataobservers = []
74 def basetype(self, basetype=None):
76 Renvoie ou met en place le type de base des objets stockés
79 return self.__basetype
81 self.__basetype = basetype
83 def store(self, value=None, **kwargs):
85 Stocke une valeur avec ses informations de filtrage.
87 if value is None: raise ValueError("Value argument required")
89 self.__values.append(copy.copy(self.__basetype(value)))
90 self.__tags.append(kwargs)
92 if self.__dynamic: self.__replots()
93 __step = len(self.__values) - 1
94 for hook, parameters, scheduler in self.__dataobservers:
95 if __step in scheduler:
96 hook( self, parameters )
98 def pop(self, item=None):
100 Retire une valeur enregistrée par son index de stockage. Sans argument,
101 retire le dernier objet enregistre.
105 self.__values.pop(__index)
106 self.__tags.pop(__index)
113 Renvoie la taille sous forme numpy du dernier objet stocké. Si c'est un
114 objet numpy, renvoie le shape. Si c'est un entier, un flottant, un
115 complexe, renvoie 1. Si c'est une liste ou un dictionnaire, renvoie la
116 longueur. Par défaut, renvoie 1.
118 if len(self.__values) > 0:
119 if self.__basetype in [numpy.matrix, numpy.ndarray, numpy.array, numpy.ravel]:
120 return self.__values[-1].shape
121 elif self.__basetype in [int, float]:
123 elif self.__basetype in [list, dict]:
124 return (len(self.__values[-1]),)
128 raise ValueError("Object has no shape before its first storage")
130 # ---------------------------------------------------------
132 "x.__str__() <==> str(x)"
133 msg = " Index Value Tags\n"
134 for i,v in enumerate(self.__values):
135 msg += " i=%05i %10s %s\n"%(i,v,self.__tags[i])
139 "x.__len__() <==> len(x)"
140 return len(self.__values)
145 def __getitem__(self, index=None ):
146 "x.__getitem__(y) <==> x[y]"
147 return copy.copy(self.__values[index])
149 def count(self, value):
150 "L.count(value) -> integer -- return number of occurrences of value"
151 return self.__values.count(value)
153 def index(self, value, start=0, stop=None):
154 "L.index(value, [start, [stop]]) -> integer -- return first index of value."
155 if stop is None : stop = len(self.__values)
156 return self.__values.index(value, start, stop)
158 # ---------------------------------------------------------
159 def __filteredIndexes(self, **kwargs):
160 "Function interne filtrant les index"
161 __indexOfFilteredItems = range(len(self.__tags))
162 __filteringKwTags = kwargs.keys()
163 if len(__filteringKwTags) > 0:
164 for tagKey in __filteringKwTags:
166 for i in __indexOfFilteredItems:
167 if tagKey in self.__tags[i]:
168 if self.__tags[i][tagKey] == kwargs[tagKey]:
170 elif isinstance(kwargs[tagKey],(list,tuple)) and self.__tags[i][tagKey] in kwargs[tagKey]:
172 __indexOfFilteredItems = __tmp
173 if len(__indexOfFilteredItems) == 0: break
174 return __indexOfFilteredItems
176 # ---------------------------------------------------------
177 def values(self, **kwargs):
178 "D.values() -> list of D's values"
179 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
180 return [self.__values[i] for i in __indexOfFilteredItems]
182 def keys(self, keyword=None , **kwargs):
183 "D.keys() -> list of D's keys"
184 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
186 for i in __indexOfFilteredItems:
187 if keyword in self.__tags[i]:
188 __keys.append( self.__tags[i][keyword] )
190 __keys.append( None )
193 def items(self, keyword=None , **kwargs):
194 "D.items() -> list of D's (key, value) pairs, as 2-tuples"
195 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
197 for i in __indexOfFilteredItems:
198 if keyword in self.__tags[i]:
199 __pairs.append( (self.__tags[i][keyword], self.__values[i]) )
201 __pairs.append( (None, self.__values[i]) )
205 "D.tagkeys() -> list of D's tag keys"
207 for dicotags in self.__tags:
208 __allKeys.extend( list(dicotags.keys()) )
209 __allKeys = sorted(set(__allKeys))
212 # def valueserie(self, item=None, allSteps=True, **kwargs):
213 # if item is not None:
214 # return self.__values[item]
216 # __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
217 # if not allSteps and len(__indexOfFilteredItems) > 0:
218 # return self.__values[__indexOfFilteredItems[0]]
220 # return [self.__values[i] for i in __indexOfFilteredItems]
222 def tagserie(self, item=None, withValues=False, outputTag=None, **kwargs):
223 "D.tagserie() -> list of D's tag serie"
225 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
227 __indexOfFilteredItems = [item,]
229 # Dans le cas où la sortie donne les valeurs d'un "outputTag"
230 if outputTag is not None and isinstance(outputTag,str) :
232 for index in __indexOfFilteredItems:
233 if outputTag in self.__tags[index].keys():
234 outputValues.append( self.__tags[index][outputTag] )
235 outputValues = sorted(set(outputValues))
238 # Dans le cas où la sortie donne les tags satisfaisants aux conditions
241 return [self.__tags[index] for index in __indexOfFilteredItems]
244 for index in __indexOfFilteredItems:
245 allTags.update( self.__tags[index] )
246 allKeys = sorted(allTags.keys())
249 # ---------------------------------------------------------
251 def stepnumber(self):
253 return len(self.__values)
256 def stepserie(self, **kwargs):
257 "Nombre de pas filtrés"
258 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
259 return __indexOfFilteredItems
262 def steplist(self, **kwargs):
263 "Nombre de pas filtrés"
264 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
265 return list(__indexOfFilteredItems)
267 # ---------------------------------------------------------
270 Renvoie la série, contenant à chaque pas, la valeur moyenne des données
271 au pas. Il faut que le type de base soit compatible avec les types
275 return [numpy.mean(item, dtype=mfp).astype('float') for item in self.__values]
277 raise TypeError("Base type is incompatible with numpy")
279 def stds(self, ddof=0):
281 Renvoie la série, contenant à chaque pas, l'écart-type des données
282 au pas. Il faut que le type de base soit compatible avec les types
285 ddof : c'est le nombre de degrés de liberté pour le calcul de
286 l'écart-type, qui est dans le diviseur. Inutile avant Numpy 1.1
289 if numpy.version.version >= '1.1.0':
290 return [numpy.array(item).std(ddof=ddof, dtype=mfp).astype('float') for item in self.__values]
292 return [numpy.array(item).std(dtype=mfp).astype('float') for item in self.__values]
294 raise TypeError("Base type is incompatible with numpy")
298 Renvoie la série, contenant à chaque pas, la somme des données au pas.
299 Il faut que le type de base soit compatible avec les types élémentaires
303 return [numpy.array(item).sum() for item in self.__values]
305 raise TypeError("Base type is incompatible with numpy")
309 Renvoie la série, contenant à chaque pas, le minimum des données au pas.
310 Il faut que le type de base soit compatible avec les types élémentaires
314 return [numpy.array(item).min() for item in self.__values]
316 raise TypeError("Base type is incompatible with numpy")
320 Renvoie la série, contenant à chaque pas, la maximum des données au pas.
321 Il faut que le type de base soit compatible avec les types élémentaires
325 return [numpy.array(item).max() for item in self.__values]
327 raise TypeError("Base type is incompatible with numpy")
334 geometry = "600x400",
338 "Préparation des plots"
340 # Vérification de la disponibilité du module Gnuplot
342 raise ImportError("The Gnuplot module is required to plot the object.")
344 # Vérification et compléments sur les paramètres d'entrée
346 Gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -persist -geometry '+geometry
348 Gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -geometry '+geometry
351 self.__g = Gnuplot.Gnuplot() # persist=1
352 self.__g('set terminal '+Gnuplot.GnuplotOpts.default_term)
353 self.__g('set style data lines')
355 self.__g('set autoscale')
356 self.__g('set xlabel "'+str(xlabel)+'"')
357 self.__g('set ylabel "'+str(ylabel)+'"')
359 self.__ltitle = ltitle
370 geometry = "600x400",
377 Renvoie un affichage de la valeur à chaque pas, si elle est compatible
378 avec un affichage Gnuplot (donc essentiellement un vecteur). Si
379 l'argument "step" existe dans la liste des pas de stockage effectués,
380 renvoie l'affichage de la valeur stockée à ce pas "step". Si l'argument
381 "item" est correct, renvoie l'affichage de la valeur stockée au numéro
382 "item". Par défaut ou en l'absence de "step" ou "item", renvoie un
383 affichage successif de tous les pas.
386 - step : valeur du pas à afficher
387 - item : index de la valeur à afficher
388 - steps : liste unique des pas de l'axe des X, ou None si c'est
389 la numérotation par défaut
390 - title : base du titre général, qui sera automatiquement
391 complétée par la mention du pas
392 - xlabel : label de l'axe des X
393 - ylabel : label de l'axe des Y
394 - ltitle : titre associé au vecteur tracé
395 - geometry : taille en pixels de la fenêtre et position du coin haut
396 gauche, au format X11 : LxH+X+Y (défaut : 600x400)
397 - filename : base de nom de fichier Postscript pour une sauvegarde,
398 qui est automatiquement complétée par le numéro du
399 fichier calculé par incrément simple de compteur
400 - dynamic : effectue un affichage des valeurs à chaque stockage
401 (au-delà du second). La méthode "plots" permet de
402 déclarer l'affichage dynamique, et c'est la méthode
403 "__replots" qui est utilisée pour l'effectuer
404 - persist : booléen indiquant que la fenêtre affichée sera
405 conservée lors du passage au dessin suivant
406 Par défaut, persist = False
407 - pause : booléen indiquant une pause après chaque tracé, et
409 Par défaut, pause = True
411 if not self.__dynamic:
412 self.__preplots(title, xlabel, ylabel, ltitle, geometry, persist, pause )
414 self.__dynamic = True
415 if len(self.__values) == 0: return 0
417 # Tracé du ou des vecteurs demandés
419 if step is not None and step < len(self.__values):
421 elif item is not None and item < len(self.__values):
424 indexes = indexes + list(range(len(self.__values)))
427 for index in indexes:
428 self.__g('set title "'+str(title)+' (pas '+str(index)+')"')
429 if isinstance(steps,list) or isinstance(steps,numpy.ndarray):
432 Steps = list(range(len(self.__values[index])))
434 self.__g.plot( Gnuplot.Data( Steps, self.__values[index], title=ltitle ) )
438 stepfilename = "%s_%03i.ps"%(filename,i)
439 if os.path.isfile(stepfilename):
440 raise ValueError("Error: a file with this name \"%s\" already exists."%stepfilename)
441 self.__g.hardcopy(filename=stepfilename, color=1)
443 eval(input('Please press return to continue...\n'))
447 Affichage dans le cas du suivi dynamique de la variable
449 if self.__dynamic and len(self.__values) < 2: return 0
451 self.__g('set title "'+str(self.__title))
452 Steps = list(range(len(self.__values)))
453 self.__g.plot( Gnuplot.Data( Steps, self.__values, title=self.__ltitle ) )
456 eval(input('Please press return to continue...\n'))
458 # ---------------------------------------------------------
459 # On pourrait aussi utiliser d'autres attributs d'un "array" comme "tofile"
462 Renvoie la moyenne sur toutes les valeurs sans tenir compte de la
463 longueur des pas. Il faut que le type de base soit compatible avec
464 les types élémentaires numpy.
467 return numpy.mean(self.__values, axis=0, dtype=mfp).astype('float')
469 raise TypeError("Base type is incompatible with numpy")
471 def std(self, ddof=0):
473 Renvoie l'écart-type de toutes les valeurs sans tenir compte de la
474 longueur des pas. Il faut que le type de base soit compatible avec
475 les types élémentaires numpy.
477 ddof : c'est le nombre de degrés de liberté pour le calcul de
478 l'écart-type, qui est dans le diviseur. Inutile avant Numpy 1.1
481 if numpy.version.version >= '1.1.0':
482 return numpy.asarray(self.__values).std(ddof=ddof,axis=0).astype('float')
484 return numpy.asarray(self.__values).std(axis=0).astype('float')
486 raise TypeError("Base type is incompatible with numpy")
490 Renvoie la somme de toutes les valeurs sans tenir compte de la
491 longueur des pas. Il faut que le type de base soit compatible avec
492 les types élémentaires numpy.
495 return numpy.asarray(self.__values).sum(axis=0)
497 raise TypeError("Base type is incompatible with numpy")
501 Renvoie le minimum de toutes les valeurs sans tenir compte de la
502 longueur des pas. Il faut que le type de base soit compatible avec
503 les types élémentaires numpy.
506 return numpy.asarray(self.__values).min(axis=0)
508 raise TypeError("Base type is incompatible with numpy")
512 Renvoie le maximum de toutes les valeurs sans tenir compte de la
513 longueur des pas. Il faut que le type de base soit compatible avec
514 les types élémentaires numpy.
517 return numpy.asarray(self.__values).max(axis=0)
519 raise TypeError("Base type is incompatible with numpy")
523 Renvoie la somme cumulée de toutes les valeurs sans tenir compte de la
524 longueur des pas. Il faut que le type de base soit compatible avec
525 les types élémentaires numpy.
528 return numpy.asarray(self.__values).cumsum(axis=0)
530 raise TypeError("Base type is incompatible with numpy")
538 geometry = "600x400",
544 Renvoie un affichage unique pour l'ensemble des valeurs à chaque pas, si
545 elles sont compatibles avec un affichage Gnuplot (donc essentiellement
546 un vecteur). Si l'argument "step" existe dans la liste des pas de
547 stockage effectués, renvoie l'affichage de la valeur stockée à ce pas
548 "step". Si l'argument "item" est correct, renvoie l'affichage de la
549 valeur stockée au numéro "item".
552 - steps : liste unique des pas de l'axe des X, ou None si c'est
553 la numérotation par défaut
554 - title : base du titre général, qui sera automatiquement
555 complétée par la mention du pas
556 - xlabel : label de l'axe des X
557 - ylabel : label de l'axe des Y
558 - ltitle : titre associé au vecteur tracé
559 - geometry : taille en pixels de la fenêtre et position du coin haut
560 gauche, au format X11 : LxH+X+Y (défaut : 600x400)
561 - filename : nom de fichier Postscript pour une sauvegarde
562 - persist : booléen indiquant que la fenêtre affichée sera
563 conservée lors du passage au dessin suivant
564 Par défaut, persist = False
565 - pause : booléen indiquant une pause après chaque tracé, et
567 Par défaut, pause = True
570 # Vérification de la disponibilité du module Gnuplot
572 raise ImportError("The Gnuplot module is required to plot the object.")
574 # Vérification et compléments sur les paramètres d'entrée
576 Gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -persist -geometry '+geometry
578 Gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -geometry '+geometry
581 if isinstance(steps,list) or isinstance(steps, numpy.ndarray):
584 Steps = list(range(len(self.__values[0])))
585 self.__g = Gnuplot.Gnuplot() # persist=1
586 self.__g('set terminal '+Gnuplot.GnuplotOpts.default_term)
587 self.__g('set style data lines')
589 self.__g('set autoscale')
590 self.__g('set title "'+str(title) +'"')
591 self.__g('set xlabel "'+str(xlabel)+'"')
592 self.__g('set ylabel "'+str(ylabel)+'"')
594 # Tracé du ou des vecteurs demandés
595 indexes = list(range(len(self.__values)))
596 self.__g.plot( Gnuplot.Data( Steps, self.__values[indexes.pop(0)], title=ltitle+" (pas 0)" ) )
597 for index in indexes:
598 self.__g.replot( Gnuplot.Data( Steps, self.__values[index], title=ltitle+" (pas %i)"%index ) )
601 self.__g.hardcopy(filename=filename, color=1)
603 eval(input('Please press return to continue...\n'))
605 # ---------------------------------------------------------
606 def setDataObserver(self, HookFunction = None, HookParameters = None, Scheduler = None):
608 Association à la variable d'un triplet définissant un observer
610 Le Scheduler attendu est une fréquence, une simple liste d'index ou un
614 # Vérification du Scheduler
615 # -------------------------
617 if isinstance(Scheduler,int): # Considéré comme une fréquence à partir de 0
618 Schedulers = range( 0, maxiter, int(Scheduler) )
619 elif isinstance(Scheduler,range): # Considéré comme un itérateur
620 Schedulers = Scheduler
621 elif isinstance(Scheduler,(list,tuple)): # Considéré comme des index explicites
622 Schedulers = [int(i) for i in Scheduler] # map( long, Scheduler )
623 else: # Dans tous les autres cas, activé par défaut
624 Schedulers = range( 0, maxiter )
626 # Stockage interne de l'observer dans la variable
627 # -----------------------------------------------
628 self.__dataobservers.append( [HookFunction, HookParameters, Schedulers] )
630 def removeDataObserver(self, HookFunction = None, AllObservers = False):
632 Suppression d'un observer nommé sur la variable.
634 On peut donner dans HookFunction la meme fonction que lors de la
635 définition, ou un simple string qui est le nom de la fonction. Si
636 AllObservers est vrai, supprime tous les observers enregistrés.
638 if hasattr(HookFunction,"func_name"):
639 name = str( HookFunction.func_name )
640 elif hasattr(HookFunction,"__name__"):
641 name = str( HookFunction.__name__ )
642 elif isinstance(HookFunction,str):
643 name = str( HookFunction )
649 for [hf, hp, hs] in self.__dataobservers:
651 if name is hf.__name__ or AllObservers: index_to_remove.append( i )
652 index_to_remove.reverse()
653 for i in index_to_remove:
654 self.__dataobservers.pop( i )
655 return len(index_to_remove)
657 def hasDataObserver(self):
658 return bool(len(self.__dataobservers) > 0)
660 # ==============================================================================
661 class SchedulerTrigger(object):
663 Classe générale d'interface de type Scheduler/Trigger
666 simplifiedCombo = None,
668 endTime = int( 1e9 ),
675 # ==============================================================================
676 class OneScalar(Persistence):
678 Classe définissant le stockage d'une valeur unique réelle (float) par pas.
680 Le type de base peut être changé par la méthode "basetype", mais il faut que
681 le nouveau type de base soit compatible avec les types par éléments de
682 numpy. On peut même utiliser cette classe pour stocker des vecteurs/listes
683 ou des matrices comme dans les classes suivantes, mais c'est déconseillé
684 pour conserver une signification claire des noms.
686 def __init__(self, name="", unit="", basetype = float):
687 Persistence.__init__(self, name, unit, basetype)
689 class OneIndex(Persistence):
691 Classe définissant le stockage d'une valeur unique entière (int) par pas.
693 def __init__(self, name="", unit="", basetype = int):
694 Persistence.__init__(self, name, unit, basetype)
696 class OneVector(Persistence):
698 Classe de stockage d'une liste de valeurs numériques homogènes par pas. Ne
699 pas utiliser cette classe pour des données hétérogènes, mais "OneList".
701 def __init__(self, name="", unit="", basetype = numpy.ravel):
702 Persistence.__init__(self, name, unit, basetype)
704 class OneMatrix(Persistence):
706 Classe de stockage d'une matrice de valeurs homogènes par pas.
708 def __init__(self, name="", unit="", basetype = numpy.matrix):
709 Persistence.__init__(self, name, unit, basetype)
711 class OneList(Persistence):
713 Classe de stockage d'une liste de valeurs hétérogènes (list) par pas. Ne
714 pas utiliser cette classe pour des données numériques homogènes, mais
717 def __init__(self, name="", unit="", basetype = list):
718 Persistence.__init__(self, name, unit, basetype)
721 "Fonction transparente, sans effet sur son argument"
724 class OneNoType(Persistence):
726 Classe de stockage d'un objet sans modification (cast) de type. Attention,
727 selon le véritable type de l'objet stocké à chaque pas, les opérations
728 arithmétiques à base de numpy peuvent être invalides ou donner des
729 résultats inattendus. Cette classe n'est donc à utiliser qu'à bon escient
730 volontairement, et pas du tout par défaut.
732 def __init__(self, name="", unit="", basetype = NoType):
733 Persistence.__init__(self, name, unit, basetype)
735 # ==============================================================================
736 class CompositePersistence(object):
738 Structure de stockage permettant de rassembler plusieurs objets de
741 Des objets par défaut sont prévus, et des objets supplémentaires peuvent
744 def __init__(self, name="", defaults=True):
748 La gestion interne des données est exclusivement basée sur les
749 variables initialisées ici (qui ne sont pas accessibles depuis
750 l'extérieur des objets comme des attributs) :
751 __StoredObjects : objets de type persistence collectés dans cet objet
753 self.__name = str(name)
755 self.__StoredObjects = {}
757 # Definition des objets par defaut
758 # --------------------------------
760 self.__StoredObjects["Informations"] = OneNoType("Informations")
761 self.__StoredObjects["Background"] = OneVector("Background", basetype=numpy.array)
762 self.__StoredObjects["BackgroundError"] = OneMatrix("BackgroundError")
763 self.__StoredObjects["Observation"] = OneVector("Observation", basetype=numpy.array)
764 self.__StoredObjects["ObservationError"] = OneMatrix("ObservationError")
765 self.__StoredObjects["Analysis"] = OneVector("Analysis", basetype=numpy.array)
766 self.__StoredObjects["AnalysisError"] = OneMatrix("AnalysisError")
767 self.__StoredObjects["Innovation"] = OneVector("Innovation", basetype=numpy.array)
768 self.__StoredObjects["KalmanGainK"] = OneMatrix("KalmanGainK")
769 self.__StoredObjects["OperatorH"] = OneMatrix("OperatorH")
770 self.__StoredObjects["RmsOMA"] = OneScalar("RmsOMA")
771 self.__StoredObjects["RmsOMB"] = OneScalar("RmsOMB")
772 self.__StoredObjects["RmsBMA"] = OneScalar("RmsBMA")
775 def store(self, name=None, value=None, **kwargs):
777 Stockage d'une valeur "value" pour le "step" dans la variable "name".
779 if name is None: raise ValueError("Storable object name is required for storage.")
780 if name not in self.__StoredObjects.keys():
781 raise ValueError("No such name '%s' exists in storable objects."%name)
782 self.__StoredObjects[name].store( value=value, **kwargs )
784 def add_object(self, name=None, persistenceType=Persistence, basetype=None ):
786 Ajoute dans les objets stockables un nouvel objet défini par son nom,
787 son type de Persistence et son type de base à chaque pas.
789 if name is None: raise ValueError("Object name is required for adding an object.")
790 if name in self.__StoredObjects.keys():
791 raise ValueError("An object with the same name '%s' already exists in storable objects. Choose another one."%name)
793 self.__StoredObjects[name] = persistenceType( name=str(name) )
795 self.__StoredObjects[name] = persistenceType( name=str(name), basetype=basetype )
797 def get_object(self, name=None ):
799 Renvoie l'objet de type Persistence qui porte le nom demandé.
801 if name is None: raise ValueError("Object name is required for retrieving an object.")
802 if name not in self.__StoredObjects.keys():
803 raise ValueError("No such name '%s' exists in stored objects."%name)
804 return self.__StoredObjects[name]
806 def set_object(self, name=None, objet=None ):
808 Affecte directement un 'objet' qui porte le nom 'name' demandé.
809 Attention, il n'est pas effectué de vérification sur le type, qui doit
810 comporter les méthodes habituelles de Persistence pour que cela
813 if name is None: raise ValueError("Object name is required for setting an object.")
814 if name in self.__StoredObjects.keys():
815 raise ValueError("An object with the same name '%s' already exists in storable objects. Choose another one."%name)
816 self.__StoredObjects[name] = objet
818 def del_object(self, name=None ):
820 Supprime un objet de la liste des objets stockables.
822 if name is None: raise ValueError("Object name is required for retrieving an object.")
823 if name not in self.__StoredObjects.keys():
824 raise ValueError("No such name '%s' exists in stored objects."%name)
825 del self.__StoredObjects[name]
827 # ---------------------------------------------------------
828 # Méthodes d'accès de type dictionnaire
829 def __getitem__(self, name=None ):
830 "x.__getitem__(y) <==> x[y]"
831 return self.get_object( name )
833 def __setitem__(self, name=None, objet=None ):
834 "x.__setitem__(i, y) <==> x[i]=y"
835 self.set_object( name, objet )
838 "D.keys() -> list of D's keys"
839 return self.get_stored_objects(hideVoidObjects = False)
842 "D.values() -> list of D's values"
843 return self.__StoredObjects.values()
846 "D.items() -> list of D's (key, value) pairs, as 2-tuples"
847 return self.__StoredObjects.items()
849 # ---------------------------------------------------------
850 def get_stored_objects(self, hideVoidObjects = False):
851 "Renvoie la liste des objets présents"
852 objs = self.__StoredObjects.keys()
857 if len(self.__StoredObjects[k]) > 0: usedObjs.append( k )
864 # ---------------------------------------------------------
865 def save_composite(self, filename=None, mode="pickle", compress="gzip"):
867 Enregistre l'objet dans le fichier indiqué selon le "mode" demandé,
868 et renvoi le nom du fichier
871 if compress == "gzip":
872 filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl.gz"
873 elif compress == "bzip2":
874 filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl.bz2"
876 filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl"
878 filename = os.path.abspath( filename )
881 if compress == "gzip":
882 output = gzip.open( filename, 'wb')
883 elif compress == "bzip2":
884 output = bz2.BZ2File( filename, 'wb')
886 output = open( filename, 'wb')
887 pickle.dump(self, output)
890 raise ValueError("Save mode '%s' unknown. Choose another one."%mode)
894 def load_composite(self, filename=None, mode="pickle", compress="gzip"):
896 Recharge un objet composite sauvé en fichier
899 raise ValueError("A file name if requested to load a composite.")
901 filename = os.path.abspath( filename )
904 if compress == "gzip":
905 pkl_file = gzip.open( filename, 'rb')
906 elif compress == "bzip2":
907 pkl_file = bz2.BZ2File( filename, 'rb')
909 pkl_file = open(filename, 'rb')
910 output = pickle.load(pkl_file)
911 for k in output.keys():
914 raise ValueError("Load mode '%s' unknown. Choose another one."%mode)
918 # ==============================================================================
919 if __name__ == "__main__":
920 print('\n AUTODIAGNOSTIC\n')