1 # -*- coding: utf-8 -*-
3 # Copyright (C) 2008-2021 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, PlatformInfo
35 mfp = PlatformInfo().MaximumPrecision()
39 if sys.version_info.major < 3:
42 import cPickle as pickle
47 # ==============================================================================
48 class Persistence(object):
50 Classe générale de persistence définissant les accesseurs nécessaires
53 def __init__(self, name="", unit="", basetype=str):
57 basetype : type de base de l'objet stocké à chaque pas
59 La gestion interne des données est exclusivement basée sur les variables
60 initialisées ici (qui ne sont pas accessibles depuis l'extérieur des
61 objets comme des attributs) :
62 __basetype : le type de base de chaque valeur, sous la forme d'un type
63 permettant l'instanciation ou le casting Python
64 __values : les valeurs de stockage. Par défaut, c'est None
66 self.__name = str(name)
67 self.__unit = str(unit)
69 self.__basetype = basetype
74 self.__dynamic = False
80 self.__dataobservers = []
82 def basetype(self, basetype=None):
84 Renvoie ou met en place le type de base des objets stockés
87 return self.__basetype
89 self.__basetype = basetype
91 def store(self, value=None, **kwargs):
93 Stocke une valeur avec ses informations de filtrage.
95 if value is None: raise ValueError("Value argument required")
97 self.__values.append(copy.copy(self.__basetype(value)))
98 self.__tags.append(kwargs)
100 if self.__dynamic: self.__replots()
101 __step = len(self.__values) - 1
102 for hook, parameters, scheduler in self.__dataobservers:
103 if __step in scheduler:
104 hook( self, parameters )
106 def pop(self, item=None):
108 Retire une valeur enregistree par son index de stockage. Sans argument,
109 retire le dernier objet enregistre.
113 self.__values.pop(__index)
114 self.__tags.pop(__index)
121 Renvoie la taille sous forme numpy du dernier objet stocké. Si c'est un
122 objet numpy, renvoie le shape. Si c'est un entier, un flottant, un
123 complexe, renvoie 1. Si c'est une liste ou un dictionnaire, renvoie la
124 longueur. Par défaut, renvoie 1.
126 if len(self.__values) > 0:
127 if self.__basetype in [numpy.matrix, numpy.ndarray, numpy.array, numpy.ravel]:
128 return self.__values[-1].shape
129 elif self.__basetype in [int, float]:
131 elif self.__basetype in [list, dict]:
132 return (len(self.__values[-1]),)
136 raise ValueError("Object has no shape before its first storage")
138 # ---------------------------------------------------------
140 "x.__str__() <==> str(x)"
141 msg = " Index Value Tags\n"
142 for i,v in enumerate(self.__values):
143 msg += " i=%05i %10s %s\n"%(i,v,self.__tags[i])
147 "x.__len__() <==> len(x)"
148 return len(self.__values)
153 def __getitem__(self, index=None ):
154 "x.__getitem__(y) <==> x[y]"
155 return copy.copy(self.__values[index])
157 def count(self, value):
158 "L.count(value) -> integer -- return number of occurrences of value"
159 return self.__values.count(value)
161 def index(self, value, start=0, stop=None):
162 "L.index(value, [start, [stop]]) -> integer -- return first index of value."
163 if stop is None : stop = len(self.__values)
164 return self.__values.index(value, start, stop)
166 # ---------------------------------------------------------
167 def __filteredIndexes(self, **kwargs):
168 "Function interne filtrant les index"
169 __indexOfFilteredItems = range(len(self.__tags))
170 __filteringKwTags = kwargs.keys()
171 if len(__filteringKwTags) > 0:
172 for tagKey in __filteringKwTags:
174 for i in __indexOfFilteredItems:
175 if tagKey in self.__tags[i]:
176 if self.__tags[i][tagKey] == kwargs[tagKey]:
178 elif isinstance(kwargs[tagKey],(list,tuple)) and self.__tags[i][tagKey] in kwargs[tagKey]:
180 __indexOfFilteredItems = __tmp
181 if len(__indexOfFilteredItems) == 0: break
182 return __indexOfFilteredItems
184 # ---------------------------------------------------------
185 def values(self, **kwargs):
186 "D.values() -> list of D's values"
187 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
188 return [self.__values[i] for i in __indexOfFilteredItems]
190 def keys(self, keyword=None , **kwargs):
191 "D.keys() -> list of D's keys"
192 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
194 for i in __indexOfFilteredItems:
195 if keyword in self.__tags[i]:
196 __keys.append( self.__tags[i][keyword] )
198 __keys.append( None )
201 def items(self, keyword=None , **kwargs):
202 "D.items() -> list of D's (key, value) pairs, as 2-tuples"
203 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
205 for i in __indexOfFilteredItems:
206 if keyword in self.__tags[i]:
207 __pairs.append( (self.__tags[i][keyword], self.__values[i]) )
209 __pairs.append( (None, self.__values[i]) )
213 "D.tagkeys() -> list of D's tag keys"
215 for dicotags in self.__tags:
216 __allKeys.extend( list(dicotags.keys()) )
217 __allKeys = sorted(set(__allKeys))
220 # def valueserie(self, item=None, allSteps=True, **kwargs):
221 # if item is not None:
222 # return self.__values[item]
224 # __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
225 # if not allSteps and len(__indexOfFilteredItems) > 0:
226 # return self.__values[__indexOfFilteredItems[0]]
228 # return [self.__values[i] for i in __indexOfFilteredItems]
230 def tagserie(self, item=None, withValues=False, outputTag=None, **kwargs):
231 "D.tagserie() -> list of D's tag serie"
233 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
235 __indexOfFilteredItems = [item,]
237 # Dans le cas où la sortie donne les valeurs d'un "outputTag"
238 if outputTag is not None and isinstance(outputTag,str) :
240 for index in __indexOfFilteredItems:
241 if outputTag in self.__tags[index].keys():
242 outputValues.append( self.__tags[index][outputTag] )
243 outputValues = sorted(set(outputValues))
246 # Dans le cas où la sortie donne les tags satisfaisants aux conditions
249 return [self.__tags[index] for index in __indexOfFilteredItems]
252 for index in __indexOfFilteredItems:
253 allTags.update( self.__tags[index] )
254 allKeys = sorted(allTags.keys())
257 # ---------------------------------------------------------
259 def stepnumber(self):
261 return len(self.__values)
264 def stepserie(self, **kwargs):
265 "Nombre de pas filtrés"
266 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
267 return __indexOfFilteredItems
270 def steplist(self, **kwargs):
271 "Nombre de pas filtrés"
272 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
273 return list(__indexOfFilteredItems)
275 # ---------------------------------------------------------
278 Renvoie la série, contenant à chaque pas, la valeur moyenne des données
279 au pas. Il faut que le type de base soit compatible avec les types
283 return [numpy.mean(item, dtype=mfp).astype('float') for item in self.__values]
285 raise TypeError("Base type is incompatible with numpy")
287 def stds(self, ddof=0):
289 Renvoie la série, contenant à chaque pas, l'écart-type des données
290 au pas. Il faut que le type de base soit compatible avec les types
293 ddof : c'est le nombre de degrés de liberté pour le calcul de
294 l'écart-type, qui est dans le diviseur. Inutile avant Numpy 1.1
297 if numpy.version.version >= '1.1.0':
298 return [numpy.array(item).std(ddof=ddof, dtype=mfp).astype('float') for item in self.__values]
300 return [numpy.array(item).std(dtype=mfp).astype('float') for item in self.__values]
302 raise TypeError("Base type is incompatible with numpy")
306 Renvoie la série, contenant à chaque pas, la somme des données au pas.
307 Il faut que le type de base soit compatible avec les types élémentaires
311 return [numpy.array(item).sum() for item in self.__values]
313 raise TypeError("Base type is incompatible with numpy")
317 Renvoie la série, contenant à chaque pas, le minimum des données au pas.
318 Il faut que le type de base soit compatible avec les types élémentaires
322 return [numpy.array(item).min() for item in self.__values]
324 raise TypeError("Base type is incompatible with numpy")
328 Renvoie la série, contenant à chaque pas, la maximum des données au pas.
329 Il faut que le type de base soit compatible avec les types élémentaires
333 return [numpy.array(item).max() for item in self.__values]
335 raise TypeError("Base type is incompatible with numpy")
342 geometry = "600x400",
346 "Préparation des plots"
348 # Vérification de la disponibilité du module Gnuplot
350 raise ImportError("The Gnuplot module is required to plot the object.")
352 # Vérification et compléments sur les paramètres d'entrée
354 Gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -persist -geometry '+geometry
356 Gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -geometry '+geometry
359 self.__g = Gnuplot.Gnuplot() # persist=1
360 self.__g('set terminal '+Gnuplot.GnuplotOpts.default_term)
361 self.__g('set style data lines')
363 self.__g('set autoscale')
364 self.__g('set xlabel "'+str(xlabel)+'"')
365 self.__g('set ylabel "'+str(ylabel)+'"')
367 self.__ltitle = ltitle
378 geometry = "600x400",
385 Renvoie un affichage de la valeur à chaque pas, si elle est compatible
386 avec un affichage Gnuplot (donc essentiellement un vecteur). Si
387 l'argument "step" existe dans la liste des pas de stockage effectués,
388 renvoie l'affichage de la valeur stockée à ce pas "step". Si l'argument
389 "item" est correct, renvoie l'affichage de la valeur stockée au numéro
390 "item". Par défaut ou en l'absence de "step" ou "item", renvoie un
391 affichage successif de tous les pas.
394 - step : valeur du pas à afficher
395 - item : index de la valeur à afficher
396 - steps : liste unique des pas de l'axe des X, ou None si c'est
397 la numérotation par défaut
398 - title : base du titre général, qui sera automatiquement
399 complétée par la mention du pas
400 - xlabel : label de l'axe des X
401 - ylabel : label de l'axe des Y
402 - ltitle : titre associé au vecteur tracé
403 - geometry : taille en pixels de la fenêtre et position du coin haut
404 gauche, au format X11 : LxH+X+Y (défaut : 600x400)
405 - filename : base de nom de fichier Postscript pour une sauvegarde,
406 qui est automatiquement complétée par le numéro du
407 fichier calculé par incrément simple de compteur
408 - dynamic : effectue un affichage des valeurs à chaque stockage
409 (au-delà du second). La méthode "plots" permet de
410 déclarer l'affichage dynamique, et c'est la méthode
411 "__replots" qui est utilisée pour l'effectuer
412 - persist : booléen indiquant que la fenêtre affichée sera
413 conservée lors du passage au dessin suivant
414 Par défaut, persist = False
415 - pause : booléen indiquant une pause après chaque tracé, et
417 Par défaut, pause = True
419 if not self.__dynamic:
420 self.__preplots(title, xlabel, ylabel, ltitle, geometry, persist, pause )
422 self.__dynamic = True
423 if len(self.__values) == 0: return 0
425 # Tracé du ou des vecteurs demandés
427 if step is not None and step < len(self.__values):
429 elif item is not None and item < len(self.__values):
432 indexes = indexes + list(range(len(self.__values)))
435 for index in indexes:
436 self.__g('set title "'+str(title)+' (pas '+str(index)+')"')
437 if isinstance(steps,list) or isinstance(steps,numpy.ndarray):
440 Steps = list(range(len(self.__values[index])))
442 self.__g.plot( Gnuplot.Data( Steps, self.__values[index], title=ltitle ) )
446 stepfilename = "%s_%03i.ps"%(filename,i)
447 if os.path.isfile(stepfilename):
448 raise ValueError("Error: a file with this name \"%s\" already exists."%stepfilename)
449 self.__g.hardcopy(filename=stepfilename, color=1)
451 eval(input('Please press return to continue...\n'))
455 Affichage dans le cas du suivi dynamique de la variable
457 if self.__dynamic and len(self.__values) < 2: return 0
459 self.__g('set title "'+str(self.__title))
460 Steps = list(range(len(self.__values)))
461 self.__g.plot( Gnuplot.Data( Steps, self.__values, title=self.__ltitle ) )
464 eval(input('Please press return to continue...\n'))
466 # ---------------------------------------------------------
467 # On pourrait aussi utiliser d'autres attributs d'un "array" comme "tofile"
470 Renvoie la moyenne sur toutes les valeurs sans tenir compte de la
471 longueur des pas. Il faut que le type de base soit compatible avec
472 les types élémentaires numpy.
475 return numpy.mean(self.__values, axis=0, dtype=mfp).astype('float')
477 raise TypeError("Base type is incompatible with numpy")
479 def std(self, ddof=0):
481 Renvoie l'écart-type de toutes les valeurs sans tenir compte de la
482 longueur des pas. Il faut que le type de base soit compatible avec
483 les types élémentaires numpy.
485 ddof : c'est le nombre de degrés de liberté pour le calcul de
486 l'écart-type, qui est dans le diviseur. Inutile avant Numpy 1.1
489 if numpy.version.version >= '1.1.0':
490 return numpy.array(self.__values).std(ddof=ddof,axis=0).astype('float')
492 return numpy.array(self.__values).std(axis=0).astype('float')
494 raise TypeError("Base type is incompatible with numpy")
498 Renvoie la somme de toutes les valeurs sans tenir compte de la
499 longueur des pas. Il faut que le type de base soit compatible avec
500 les types élémentaires numpy.
503 return numpy.array(self.__values).sum(axis=0)
505 raise TypeError("Base type is incompatible with numpy")
509 Renvoie le minimum de toutes les valeurs sans tenir compte de la
510 longueur des pas. Il faut que le type de base soit compatible avec
511 les types élémentaires numpy.
514 return numpy.array(self.__values).min(axis=0)
516 raise TypeError("Base type is incompatible with numpy")
520 Renvoie le maximum de toutes les valeurs sans tenir compte de la
521 longueur des pas. Il faut que le type de base soit compatible avec
522 les types élémentaires numpy.
525 return numpy.array(self.__values).max(axis=0)
527 raise TypeError("Base type is incompatible with numpy")
531 Renvoie la somme cumulée de toutes les valeurs sans tenir compte de la
532 longueur des pas. Il faut que le type de base soit compatible avec
533 les types élémentaires numpy.
536 return numpy.array(self.__values).cumsum(axis=0)
538 raise TypeError("Base type is incompatible with numpy")
546 geometry = "600x400",
552 Renvoie un affichage unique pour l'ensemble des valeurs à chaque pas, si
553 elles sont compatibles avec un affichage Gnuplot (donc essentiellement
554 un vecteur). Si l'argument "step" existe dans la liste des pas de
555 stockage effectués, renvoie l'affichage de la valeur stockée à ce pas
556 "step". Si l'argument "item" est correct, renvoie l'affichage de la
557 valeur stockée au numéro "item".
560 - steps : liste unique des pas de l'axe des X, ou None si c'est
561 la numérotation par défaut
562 - title : base du titre général, qui sera automatiquement
563 complétée par la mention du pas
564 - xlabel : label de l'axe des X
565 - ylabel : label de l'axe des Y
566 - ltitle : titre associé au vecteur tracé
567 - geometry : taille en pixels de la fenêtre et position du coin haut
568 gauche, au format X11 : LxH+X+Y (défaut : 600x400)
569 - filename : nom de fichier Postscript pour une sauvegarde
570 - persist : booléen indiquant que la fenêtre affichée sera
571 conservée lors du passage au dessin suivant
572 Par défaut, persist = False
573 - pause : booléen indiquant une pause après chaque tracé, et
575 Par défaut, pause = True
578 # Vérification de la disponibilité du module Gnuplot
580 raise ImportError("The Gnuplot module is required to plot the object.")
582 # Vérification et compléments sur les paramètres d'entrée
584 Gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -persist -geometry '+geometry
586 Gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -geometry '+geometry
589 if isinstance(steps,list) or isinstance(steps, numpy.ndarray):
592 Steps = list(range(len(self.__values[0])))
593 self.__g = Gnuplot.Gnuplot() # persist=1
594 self.__g('set terminal '+Gnuplot.GnuplotOpts.default_term)
595 self.__g('set style data lines')
597 self.__g('set autoscale')
598 self.__g('set title "'+str(title) +'"')
599 self.__g('set xlabel "'+str(xlabel)+'"')
600 self.__g('set ylabel "'+str(ylabel)+'"')
602 # Tracé du ou des vecteurs demandés
603 indexes = list(range(len(self.__values)))
604 self.__g.plot( Gnuplot.Data( Steps, self.__values[indexes.pop(0)], title=ltitle+" (pas 0)" ) )
605 for index in indexes:
606 self.__g.replot( Gnuplot.Data( Steps, self.__values[index], title=ltitle+" (pas %i)"%index ) )
609 self.__g.hardcopy(filename=filename, color=1)
611 eval(input('Please press return to continue...\n'))
613 # ---------------------------------------------------------
614 def setDataObserver(self, HookFunction = None, HookParameters = None, Scheduler = None):
616 Association à la variable d'un triplet définissant un observer
618 Le Scheduler attendu est une fréquence, une simple liste d'index ou un
622 # Vérification du Scheduler
623 # -------------------------
625 if isinstance(Scheduler,int): # Considéré comme une fréquence à partir de 0
626 Schedulers = range( 0, maxiter, int(Scheduler) )
627 elif isinstance(Scheduler,range): # Considéré comme un itérateur
628 Schedulers = Scheduler
629 elif isinstance(Scheduler,(list,tuple)): # Considéré comme des index explicites
630 Schedulers = [iLong(i) for i in Scheduler] # map( long, Scheduler )
631 else: # Dans tous les autres cas, activé par défaut
632 Schedulers = range( 0, maxiter )
634 # Stockage interne de l'observer dans la variable
635 # -----------------------------------------------
636 self.__dataobservers.append( [HookFunction, HookParameters, Schedulers] )
638 def removeDataObserver(self, HookFunction = None, AllObservers = False):
640 Suppression d'un observer nommé sur la variable.
642 On peut donner dans HookFunction la meme fonction que lors de la
643 définition, ou un simple string qui est le nom de la fonction. Si
644 AllObservers est vrai, supprime tous les observers enregistrés.
646 if hasattr(HookFunction,"func_name"):
647 name = str( HookFunction.func_name )
648 elif hasattr(HookFunction,"__name__"):
649 name = str( HookFunction.__name__ )
650 elif isinstance(HookFunction,str):
651 name = str( HookFunction )
657 for [hf, hp, hs] in self.__dataobservers:
659 if name is hf.__name__ or AllObservers: index_to_remove.append( i )
660 index_to_remove.reverse()
661 for i in index_to_remove:
662 self.__dataobservers.pop( i )
663 return len(index_to_remove)
665 def hasDataObserver(self):
666 return bool(len(self.__dataobservers) > 0)
668 # ==============================================================================
669 class SchedulerTrigger(object):
671 Classe générale d'interface de type Scheduler/Trigger
674 simplifiedCombo = None,
676 endTime = int( 1e9 ),
683 # ==============================================================================
684 class OneScalar(Persistence):
686 Classe définissant le stockage d'une valeur unique réelle (float) par pas.
688 Le type de base peut être changé par la méthode "basetype", mais il faut que
689 le nouveau type de base soit compatible avec les types par éléments de
690 numpy. On peut même utiliser cette classe pour stocker des vecteurs/listes
691 ou des matrices comme dans les classes suivantes, mais c'est déconseillé
692 pour conserver une signification claire des noms.
694 def __init__(self, name="", unit="", basetype = float):
695 Persistence.__init__(self, name, unit, basetype)
697 class OneIndex(Persistence):
699 Classe définissant le stockage d'une valeur unique entière (int) par pas.
701 def __init__(self, name="", unit="", basetype = int):
702 Persistence.__init__(self, name, unit, basetype)
704 class OneVector(Persistence):
706 Classe de stockage d'une liste de valeurs numériques homogènes par pas. Ne
707 pas utiliser cette classe pour des données hétérogènes, mais "OneList".
709 def __init__(self, name="", unit="", basetype = numpy.ravel):
710 Persistence.__init__(self, name, unit, basetype)
712 class OneMatrix(Persistence):
714 Classe de stockage d'une matrice de valeurs (numpy.matrix) par pas.
716 def __init__(self, name="", unit="", basetype = numpy.matrix):
717 Persistence.__init__(self, name, unit, basetype)
719 class OneList(Persistence):
721 Classe de stockage d'une liste de valeurs hétérogènes (list) par pas. Ne pas
722 utiliser cette classe pour des données numériques homogènes, mais
725 def __init__(self, name="", unit="", basetype = list):
726 Persistence.__init__(self, name, unit, basetype)
729 "Fonction transparente, sans effet sur son argument"
732 class OneNoType(Persistence):
734 Classe de stockage d'un objet sans modification (cast) de type. Attention,
735 selon le véritable type de l'objet stocké à chaque pas, les opérations
736 arithmétiques à base de numpy peuvent être invalides ou donner des résultats
737 inattendus. Cette classe n'est donc à utiliser qu'à bon escient
738 volontairement, et pas du tout par défaut.
740 def __init__(self, name="", unit="", basetype = NoType):
741 Persistence.__init__(self, name, unit, basetype)
743 # ==============================================================================
744 class CompositePersistence(object):
746 Structure de stockage permettant de rassembler plusieurs objets de
749 Des objets par défaut sont prévus, et des objets supplémentaires peuvent
752 def __init__(self, name="", defaults=True):
756 La gestion interne des données est exclusivement basée sur les variables
757 initialisées ici (qui ne sont pas accessibles depuis l'extérieur des
758 objets comme des attributs) :
759 __StoredObjects : objets de type persistence collectés dans cet objet
761 self.__name = str(name)
763 self.__StoredObjects = {}
765 # Definition des objets par defaut
766 # --------------------------------
768 self.__StoredObjects["Informations"] = OneNoType("Informations")
769 self.__StoredObjects["Background"] = OneVector("Background", basetype=numpy.array)
770 self.__StoredObjects["BackgroundError"] = OneMatrix("BackgroundError")
771 self.__StoredObjects["Observation"] = OneVector("Observation", basetype=numpy.array)
772 self.__StoredObjects["ObservationError"] = OneMatrix("ObservationError")
773 self.__StoredObjects["Analysis"] = OneVector("Analysis", basetype=numpy.array)
774 self.__StoredObjects["AnalysisError"] = OneMatrix("AnalysisError")
775 self.__StoredObjects["Innovation"] = OneVector("Innovation", basetype=numpy.array)
776 self.__StoredObjects["KalmanGainK"] = OneMatrix("KalmanGainK")
777 self.__StoredObjects["OperatorH"] = OneMatrix("OperatorH")
778 self.__StoredObjects["RmsOMA"] = OneScalar("RmsOMA")
779 self.__StoredObjects["RmsOMB"] = OneScalar("RmsOMB")
780 self.__StoredObjects["RmsBMA"] = OneScalar("RmsBMA")
783 def store(self, name=None, value=None, **kwargs):
785 Stockage d'une valeur "value" pour le "step" dans la variable "name".
787 if name is None: raise ValueError("Storable object name is required for storage.")
788 if name not in self.__StoredObjects.keys():
789 raise ValueError("No such name '%s' exists in storable objects."%name)
790 self.__StoredObjects[name].store( value=value, **kwargs )
792 def add_object(self, name=None, persistenceType=Persistence, basetype=None ):
794 Ajoute dans les objets stockables un nouvel objet défini par son nom, son
795 type de Persistence et son type de base à chaque pas.
797 if name is None: raise ValueError("Object name is required for adding an object.")
798 if name in self.__StoredObjects.keys():
799 raise ValueError("An object with the same name '%s' already exists in storable objects. Choose another one."%name)
801 self.__StoredObjects[name] = persistenceType( name=str(name) )
803 self.__StoredObjects[name] = persistenceType( name=str(name), basetype=basetype )
805 def get_object(self, name=None ):
807 Renvoie l'objet de type Persistence qui porte le nom demandé.
809 if name is None: raise ValueError("Object name is required for retrieving an object.")
810 if name not in self.__StoredObjects.keys():
811 raise ValueError("No such name '%s' exists in stored objects."%name)
812 return self.__StoredObjects[name]
814 def set_object(self, name=None, objet=None ):
816 Affecte directement un 'objet' qui porte le nom 'name' demandé.
817 Attention, il n'est pas effectué de vérification sur le type, qui doit
818 comporter les méthodes habituelles de Persistence pour que cela
821 if name is None: raise ValueError("Object name is required for setting an object.")
822 if name in self.__StoredObjects.keys():
823 raise ValueError("An object with the same name '%s' already exists in storable objects. Choose another one."%name)
824 self.__StoredObjects[name] = objet
826 def del_object(self, name=None ):
828 Supprime un objet de la liste des objets stockables.
830 if name is None: raise ValueError("Object name is required for retrieving an object.")
831 if name not in self.__StoredObjects.keys():
832 raise ValueError("No such name '%s' exists in stored objects."%name)
833 del self.__StoredObjects[name]
835 # ---------------------------------------------------------
836 # Méthodes d'accès de type dictionnaire
837 def __getitem__(self, name=None ):
838 "x.__getitem__(y) <==> x[y]"
839 return self.get_object( name )
841 def __setitem__(self, name=None, objet=None ):
842 "x.__setitem__(i, y) <==> x[i]=y"
843 self.set_object( name, objet )
846 "D.keys() -> list of D's keys"
847 return self.get_stored_objects(hideVoidObjects = False)
850 "D.values() -> list of D's values"
851 return self.__StoredObjects.values()
854 "D.items() -> list of D's (key, value) pairs, as 2-tuples"
855 return self.__StoredObjects.items()
857 # ---------------------------------------------------------
858 def get_stored_objects(self, hideVoidObjects = False):
859 "Renvoie la liste des objets présents"
860 objs = self.__StoredObjects.keys()
865 if len(self.__StoredObjects[k]) > 0: usedObjs.append( k )
872 # ---------------------------------------------------------
873 def save_composite(self, filename=None, mode="pickle", compress="gzip"):
875 Enregistre l'objet dans le fichier indiqué selon le "mode" demandé,
876 et renvoi le nom du fichier
879 if compress == "gzip":
880 filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl.gz"
881 elif compress == "bzip2":
882 filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl.bz2"
884 filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl"
886 filename = os.path.abspath( filename )
889 if compress == "gzip":
890 output = gzip.open( filename, 'wb')
891 elif compress == "bzip2":
892 output = bz2.BZ2File( filename, 'wb')
894 output = open( filename, 'wb')
895 pickle.dump(self, output)
898 raise ValueError("Save mode '%s' unknown. Choose another one."%mode)
902 def load_composite(self, filename=None, mode="pickle", compress="gzip"):
904 Recharge un objet composite sauvé en fichier
907 raise ValueError("A file name if requested to load a composite.")
909 filename = os.path.abspath( filename )
912 if compress == "gzip":
913 pkl_file = gzip.open( filename, 'rb')
914 elif compress == "bzip2":
915 pkl_file = bz2.BZ2File( filename, 'rb')
917 pkl_file = open(filename, 'rb')
918 output = pickle.load(pkl_file)
919 for k in output.keys():
922 raise ValueError("Load mode '%s' unknown. Choose another one."%mode)
926 # ==============================================================================
927 if __name__ == "__main__":
928 print('\n AUTODIAGNOSTIC\n')