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, 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) 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) for item in self.__values]
300 return [numpy.array(item).std() 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 if self.__basetype in [int, float]:
476 return float( numpy.mean(self.__values, dtype=mfp) )
478 return numpy.mean(self.__values, axis=0, dtype=mfp)
480 raise TypeError("Base type is incompatible with numpy")
482 def std(self, ddof=0):
484 Renvoie l'écart-type de toutes les valeurs sans tenir compte de la
485 longueur des pas. Il faut que le type de base soit compatible avec
486 les types élémentaires numpy.
488 ddof : c'est le nombre de degrés de liberté pour le calcul de
489 l'écart-type, qui est dans le diviseur. Inutile avant Numpy 1.1
492 if numpy.version.version >= '1.1.0':
493 return numpy.array(self.__values).std(ddof=ddof,axis=0)
495 return numpy.array(self.__values).std(axis=0)
497 raise TypeError("Base type is incompatible with numpy")
501 Renvoie la somme 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.array(self.__values).sum(axis=0)
508 raise TypeError("Base type is incompatible with numpy")
512 Renvoie le minimum 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.array(self.__values).min(axis=0)
519 raise TypeError("Base type is incompatible with numpy")
523 Renvoie le maximum 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.array(self.__values).max(axis=0)
530 raise TypeError("Base type is incompatible with numpy")
534 Renvoie la somme cumulée de toutes les valeurs sans tenir compte de la
535 longueur des pas. Il faut que le type de base soit compatible avec
536 les types élémentaires numpy.
539 return numpy.array(self.__values).cumsum(axis=0)
541 raise TypeError("Base type is incompatible with numpy")
549 geometry = "600x400",
555 Renvoie un affichage unique pour l'ensemble des valeurs à chaque pas, si
556 elles sont compatibles avec un affichage Gnuplot (donc essentiellement
557 un vecteur). Si l'argument "step" existe dans la liste des pas de
558 stockage effectués, renvoie l'affichage de la valeur stockée à ce pas
559 "step". Si l'argument "item" est correct, renvoie l'affichage de la
560 valeur stockée au numéro "item".
563 - steps : liste unique des pas de l'axe des X, ou None si c'est
564 la numérotation par défaut
565 - title : base du titre général, qui sera automatiquement
566 complétée par la mention du pas
567 - xlabel : label de l'axe des X
568 - ylabel : label de l'axe des Y
569 - ltitle : titre associé au vecteur tracé
570 - geometry : taille en pixels de la fenêtre et position du coin haut
571 gauche, au format X11 : LxH+X+Y (défaut : 600x400)
572 - filename : nom de fichier Postscript pour une sauvegarde
573 - persist : booléen indiquant que la fenêtre affichée sera
574 conservée lors du passage au dessin suivant
575 Par défaut, persist = False
576 - pause : booléen indiquant une pause après chaque tracé, et
578 Par défaut, pause = True
581 # Vérification de la disponibilité du module Gnuplot
583 raise ImportError("The Gnuplot module is required to plot the object.")
585 # Vérification et compléments sur les paramètres d'entrée
587 Gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -persist -geometry '+geometry
589 Gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -geometry '+geometry
592 if isinstance(steps,list) or isinstance(steps, numpy.ndarray):
595 Steps = list(range(len(self.__values[0])))
596 self.__g = Gnuplot.Gnuplot() # persist=1
597 self.__g('set terminal '+Gnuplot.GnuplotOpts.default_term)
598 self.__g('set style data lines')
600 self.__g('set autoscale')
601 self.__g('set title "'+str(title) +'"')
602 self.__g('set xlabel "'+str(xlabel)+'"')
603 self.__g('set ylabel "'+str(ylabel)+'"')
605 # Tracé du ou des vecteurs demandés
606 indexes = list(range(len(self.__values)))
607 self.__g.plot( Gnuplot.Data( Steps, self.__values[indexes.pop(0)], title=ltitle+" (pas 0)" ) )
608 for index in indexes:
609 self.__g.replot( Gnuplot.Data( Steps, self.__values[index], title=ltitle+" (pas %i)"%index ) )
612 self.__g.hardcopy(filename=filename, color=1)
614 eval(input('Please press return to continue...\n'))
616 # ---------------------------------------------------------
617 def setDataObserver(self, HookFunction = None, HookParameters = None, Scheduler = None):
619 Association à la variable d'un triplet définissant un observer
621 Le Scheduler attendu est une fréquence, une simple liste d'index ou un
625 # Vérification du Scheduler
626 # -------------------------
628 if isinstance(Scheduler,int): # Considéré comme une fréquence à partir de 0
629 Schedulers = range( 0, maxiter, int(Scheduler) )
630 elif isinstance(Scheduler,range): # Considéré comme un itérateur
631 Schedulers = Scheduler
632 elif isinstance(Scheduler,(list,tuple)): # Considéré comme des index explicites
633 Schedulers = [iLong(i) for i in Scheduler] # map( long, Scheduler )
634 else: # Dans tous les autres cas, activé par défaut
635 Schedulers = range( 0, maxiter )
637 # Stockage interne de l'observer dans la variable
638 # -----------------------------------------------
639 self.__dataobservers.append( [HookFunction, HookParameters, Schedulers] )
641 def removeDataObserver(self, HookFunction = None, AllObservers = False):
643 Suppression d'un observer nommé sur la variable.
645 On peut donner dans HookFunction la meme fonction que lors de la
646 définition, ou un simple string qui est le nom de la fonction. Si
647 AllObservers est vrai, supprime tous les observers enregistrés.
649 if hasattr(HookFunction,"func_name"):
650 name = str( HookFunction.func_name )
651 elif hasattr(HookFunction,"__name__"):
652 name = str( HookFunction.__name__ )
653 elif isinstance(HookFunction,str):
654 name = str( HookFunction )
660 for [hf, hp, hs] in self.__dataobservers:
662 if name is hf.__name__ or AllObservers: index_to_remove.append( i )
663 index_to_remove.reverse()
664 for i in index_to_remove:
665 self.__dataobservers.pop( i )
666 return len(index_to_remove)
668 def hasDataObserver(self):
669 return bool(len(self.__dataobservers) > 0)
671 # ==============================================================================
672 class SchedulerTrigger(object):
674 Classe générale d'interface de type Scheduler/Trigger
677 simplifiedCombo = None,
679 endTime = int( 1e9 ),
686 # ==============================================================================
687 class OneScalar(Persistence):
689 Classe définissant le stockage d'une valeur unique réelle (float) par pas.
691 Le type de base peut être changé par la méthode "basetype", mais il faut que
692 le nouveau type de base soit compatible avec les types par éléments de
693 numpy. On peut même utiliser cette classe pour stocker des vecteurs/listes
694 ou des matrices comme dans les classes suivantes, mais c'est déconseillé
695 pour conserver une signification claire des noms.
697 def __init__(self, name="", unit="", basetype = float):
698 Persistence.__init__(self, name, unit, basetype)
700 class OneIndex(Persistence):
702 Classe définissant le stockage d'une valeur unique entière (int) par pas.
704 def __init__(self, name="", unit="", basetype = int):
705 Persistence.__init__(self, name, unit, basetype)
707 class OneVector(Persistence):
709 Classe de stockage d'une liste de valeurs numériques homogènes par pas. Ne
710 pas utiliser cette classe pour des données hétérogènes, mais "OneList".
712 def __init__(self, name="", unit="", basetype = numpy.ravel):
713 Persistence.__init__(self, name, unit, basetype)
715 class OneMatrix(Persistence):
717 Classe de stockage d'une matrice de valeurs (numpy.matrix) par pas.
719 def __init__(self, name="", unit="", basetype = numpy.matrix):
720 Persistence.__init__(self, name, unit, basetype)
722 class OneList(Persistence):
724 Classe de stockage d'une liste de valeurs hétérogènes (list) par pas. Ne pas
725 utiliser cette classe pour des données numériques homogènes, mais
728 def __init__(self, name="", unit="", basetype = list):
729 Persistence.__init__(self, name, unit, basetype)
732 "Fonction transparente, sans effet sur son argument"
735 class OneNoType(Persistence):
737 Classe de stockage d'un objet sans modification (cast) de type. Attention,
738 selon le véritable type de l'objet stocké à chaque pas, les opérations
739 arithmétiques à base de numpy peuvent être invalides ou donner des résultats
740 inattendus. Cette classe n'est donc à utiliser qu'à bon escient
741 volontairement, et pas du tout par défaut.
743 def __init__(self, name="", unit="", basetype = NoType):
744 Persistence.__init__(self, name, unit, basetype)
746 # ==============================================================================
747 class CompositePersistence(object):
749 Structure de stockage permettant de rassembler plusieurs objets de
752 Des objets par défaut sont prévus, et des objets supplémentaires peuvent
755 def __init__(self, name="", defaults=True):
759 La gestion interne des données est exclusivement basée sur les variables
760 initialisées ici (qui ne sont pas accessibles depuis l'extérieur des
761 objets comme des attributs) :
762 __StoredObjects : objets de type persistence collectés dans cet objet
764 self.__name = str(name)
766 self.__StoredObjects = {}
768 # Definition des objets par defaut
769 # --------------------------------
771 self.__StoredObjects["Informations"] = OneNoType("Informations")
772 self.__StoredObjects["Background"] = OneVector("Background", basetype=numpy.array)
773 self.__StoredObjects["BackgroundError"] = OneMatrix("BackgroundError")
774 self.__StoredObjects["Observation"] = OneVector("Observation", basetype=numpy.array)
775 self.__StoredObjects["ObservationError"] = OneMatrix("ObservationError")
776 self.__StoredObjects["Analysis"] = OneVector("Analysis", basetype=numpy.array)
777 self.__StoredObjects["AnalysisError"] = OneMatrix("AnalysisError")
778 self.__StoredObjects["Innovation"] = OneVector("Innovation", basetype=numpy.array)
779 self.__StoredObjects["KalmanGainK"] = OneMatrix("KalmanGainK")
780 self.__StoredObjects["OperatorH"] = OneMatrix("OperatorH")
781 self.__StoredObjects["RmsOMA"] = OneScalar("RmsOMA")
782 self.__StoredObjects["RmsOMB"] = OneScalar("RmsOMB")
783 self.__StoredObjects["RmsBMA"] = OneScalar("RmsBMA")
786 def store(self, name=None, value=None, **kwargs):
788 Stockage d'une valeur "value" pour le "step" dans la variable "name".
790 if name is None: raise ValueError("Storable object name is required for storage.")
791 if name not in self.__StoredObjects.keys():
792 raise ValueError("No such name '%s' exists in storable objects."%name)
793 self.__StoredObjects[name].store( value=value, **kwargs )
795 def add_object(self, name=None, persistenceType=Persistence, basetype=None ):
797 Ajoute dans les objets stockables un nouvel objet défini par son nom, son
798 type de Persistence et son type de base à chaque pas.
800 if name is None: raise ValueError("Object name is required for adding an object.")
801 if name in self.__StoredObjects.keys():
802 raise ValueError("An object with the same name '%s' already exists in storable objects. Choose another one."%name)
804 self.__StoredObjects[name] = persistenceType( name=str(name) )
806 self.__StoredObjects[name] = persistenceType( name=str(name), basetype=basetype )
808 def get_object(self, name=None ):
810 Renvoie l'objet de type Persistence qui porte le nom demandé.
812 if name is None: raise ValueError("Object name is required for retrieving an object.")
813 if name not in self.__StoredObjects.keys():
814 raise ValueError("No such name '%s' exists in stored objects."%name)
815 return self.__StoredObjects[name]
817 def set_object(self, name=None, objet=None ):
819 Affecte directement un 'objet' qui porte le nom 'name' demandé.
820 Attention, il n'est pas effectué de vérification sur le type, qui doit
821 comporter les méthodes habituelles de Persistence pour que cela
824 if name is None: raise ValueError("Object name is required for setting an object.")
825 if name in self.__StoredObjects.keys():
826 raise ValueError("An object with the same name '%s' already exists in storable objects. Choose another one."%name)
827 self.__StoredObjects[name] = objet
829 def del_object(self, name=None ):
831 Supprime un objet de la liste des objets stockables.
833 if name is None: raise ValueError("Object name is required for retrieving an object.")
834 if name not in self.__StoredObjects.keys():
835 raise ValueError("No such name '%s' exists in stored objects."%name)
836 del self.__StoredObjects[name]
838 # ---------------------------------------------------------
839 # Méthodes d'accès de type dictionnaire
840 def __getitem__(self, name=None ):
841 "x.__getitem__(y) <==> x[y]"
842 return self.get_object( name )
844 def __setitem__(self, name=None, objet=None ):
845 "x.__setitem__(i, y) <==> x[i]=y"
846 self.set_object( name, objet )
849 "D.keys() -> list of D's keys"
850 return self.get_stored_objects(hideVoidObjects = False)
853 "D.values() -> list of D's values"
854 return self.__StoredObjects.values()
857 "D.items() -> list of D's (key, value) pairs, as 2-tuples"
858 return self.__StoredObjects.items()
860 # ---------------------------------------------------------
861 def get_stored_objects(self, hideVoidObjects = False):
862 "Renvoie la liste des objets présents"
863 objs = self.__StoredObjects.keys()
868 if len(self.__StoredObjects[k]) > 0: usedObjs.append( k )
875 # ---------------------------------------------------------
876 def save_composite(self, filename=None, mode="pickle", compress="gzip"):
878 Enregistre l'objet dans le fichier indiqué selon le "mode" demandé,
879 et renvoi le nom du fichier
882 if compress == "gzip":
883 filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl.gz"
884 elif compress == "bzip2":
885 filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl.bz2"
887 filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl"
889 filename = os.path.abspath( filename )
892 if compress == "gzip":
893 output = gzip.open( filename, 'wb')
894 elif compress == "bzip2":
895 output = bz2.BZ2File( filename, 'wb')
897 output = open( filename, 'wb')
898 pickle.dump(self, output)
901 raise ValueError("Save mode '%s' unknown. Choose another one."%mode)
905 def load_composite(self, filename=None, mode="pickle", compress="gzip"):
907 Recharge un objet composite sauvé en fichier
910 raise ValueError("A file name if requested to load a composite.")
912 filename = os.path.abspath( filename )
915 if compress == "gzip":
916 pkl_file = gzip.open( filename, 'rb')
917 elif compress == "bzip2":
918 pkl_file = bz2.BZ2File( filename, 'rb')
920 pkl_file = open(filename, 'rb')
921 output = pickle.load(pkl_file)
922 for k in output.keys():
925 raise ValueError("Load mode '%s' unknown. Choose another one."%mode)
929 # ==============================================================================
930 if __name__ == "__main__":
931 print('\n AUTODIAGNOSTIC\n')