1 # -*- coding: utf-8 -*-
3 # Copyright (C) 2008-2018 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 sys, numpy, copy
32 from daCore.PlatformInfo import PathManagement ; PathManagement()
34 if sys.version_info.major < 3:
37 import cPickle as pickle
42 # ==============================================================================
43 class Persistence(object):
45 Classe générale de persistence définissant les accesseurs nécessaires
48 def __init__(self, name="", unit="", basetype=str):
52 basetype : type de base de l'objet stocké à chaque pas
54 La gestion interne des données est exclusivement basée sur les variables
55 initialisées ici (qui ne sont pas accessibles depuis l'extérieur des
56 objets comme des attributs) :
57 __basetype : le type de base de chaque valeur, sous la forme d'un type
58 permettant l'instanciation ou le casting Python
59 __values : les valeurs de stockage. Par défaut, c'est None
61 self.__name = str(name)
62 self.__unit = str(unit)
64 self.__basetype = basetype
69 self.__dynamic = False
76 self.__dataobservers = []
78 def basetype(self, basetype=None):
80 Renvoie ou met en place le type de base des objets stockés
83 return self.__basetype
85 self.__basetype = basetype
87 def store(self, value=None, **kwargs):
89 Stocke une valeur avec ses informations de filtrage.
91 if value is None: raise ValueError("Value argument required")
93 self.__values.append(copy.copy(self.__basetype(value)))
94 self.__tags.append(kwargs)
96 if self.__dynamic: self.__replots()
97 __step = len(self.__values) - 1
98 for hook, parameters, scheduler in self.__dataobservers:
99 if __step in scheduler:
100 hook( self, parameters )
102 def pop(self, item=None):
104 Retire une valeur enregistree par son index de stockage. Sans argument,
105 retire le dernier objet enregistre.
109 self.__values.pop(__index)
110 self.__tags.pop(__index)
117 Renvoie la taille sous forme numpy du dernier objet stocké. Si c'est un
118 objet numpy, renvoie le shape. Si c'est un entier, un flottant, un
119 complexe, renvoie 1. Si c'est une liste ou un dictionnaire, renvoie la
120 longueur. Par défaut, renvoie 1.
122 if len(self.__values) > 0:
123 if self.__basetype in [numpy.matrix, numpy.ndarray, numpy.array, numpy.ravel]:
124 return self.__values[-1].shape
125 elif self.__basetype in [int, float]:
127 elif self.__basetype in [list, dict]:
128 return (len(self.__values[-1]),)
132 raise ValueError("Object has no shape before its first storage")
134 # ---------------------------------------------------------
136 "x.__str__() <==> str(x)"
137 msg = " Index Value Tags\n"
138 for i,v in enumerate(self.__values):
139 msg += " i=%05i %10s %s\n"%(i,v,self.__tags[i])
143 "x.__len__() <==> len(x)"
144 return len(self.__values)
146 def __getitem__(self, index=None ):
147 "x.__getitem__(y) <==> x[y]"
148 return copy.copy(self.__values[index])
150 def count(self, value):
151 "L.count(value) -> integer -- return number of occurrences of value"
152 return self.__values.count(value)
154 def index(self, value, start=0, stop=None):
155 "L.index(value, [start, [stop]]) -> integer -- return first index of value."
156 if stop is None : stop = len(self.__values)
157 return self.__values.index(value, start, stop)
159 # ---------------------------------------------------------
160 def __filteredIndexes(self, **kwargs):
161 "Function interne filtrant les index"
162 __indexOfFilteredItems = range(len(self.__tags))
163 __filteringKwTags = kwargs.keys()
164 if len(__filteringKwTags) > 0:
165 for tagKey in __filteringKwTags:
167 for i in __indexOfFilteredItems:
168 if tagKey in self.__tags[i]:
169 if self.__tags[i][tagKey] == kwargs[tagKey]:
171 elif isinstance(kwargs[tagKey],(list,tuple)) and self.__tags[i][tagKey] in kwargs[tagKey]:
173 __indexOfFilteredItems = __tmp
174 if len(__indexOfFilteredItems) == 0: break
175 return __indexOfFilteredItems
177 # ---------------------------------------------------------
178 def values(self, **kwargs):
179 "D.values() -> list of D's values"
180 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
181 return [self.__values[i] for i in __indexOfFilteredItems]
183 def keys(self, keyword=None , **kwargs):
184 "D.keys() -> list of D's keys"
185 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
187 for i in __indexOfFilteredItems:
188 if keyword in self.__tags[i]:
189 __keys.append( self.__tags[i][keyword] )
191 __keys.append( None )
194 def items(self, keyword=None , **kwargs):
195 "D.items() -> list of D's (key, value) pairs, as 2-tuples"
196 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
198 for i in __indexOfFilteredItems:
199 if keyword in self.__tags[i]:
200 __pairs.append( (self.__tags[i][keyword], self.__values[i]) )
202 __pairs.append( (None, self.__values[i]) )
206 "D.tagkeys() -> list of D's tag keys"
208 for dicotags in self.__tags:
209 __allKeys.extend( list(dicotags.keys()) )
210 __allKeys = sorted(set(__allKeys))
213 # def valueserie(self, item=None, allSteps=True, **kwargs):
214 # if item is not None:
215 # return self.__values[item]
217 # __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
218 # if not allSteps and len(__indexOfFilteredItems) > 0:
219 # return self.__values[__indexOfFilteredItems[0]]
221 # return [self.__values[i] for i in __indexOfFilteredItems]
223 def tagserie(self, item=None, withValues=False, outputTag=None, **kwargs):
224 "D.tagserie() -> list of D's tag serie"
226 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
228 __indexOfFilteredItems = [item,]
230 # Dans le cas où la sortie donne les valeurs d'un "outputTag"
231 if outputTag is not None and isinstance(outputTag,str) :
233 for index in __indexOfFilteredItems:
234 if outputTag in self.__tags[index].keys():
235 outputValues.append( self.__tags[index][outputTag] )
236 outputValues = sorted(set(outputValues))
239 # Dans le cas où la sortie donne les tags satisfaisants aux conditions
242 return [self.__tags[index] for index in __indexOfFilteredItems]
245 for index in __indexOfFilteredItems:
246 allTags.update( self.__tags[index] )
247 allKeys = sorted(allTags.keys())
250 # ---------------------------------------------------------
252 def stepnumber(self):
254 return len(self.__values)
257 def stepserie(self, **kwargs):
258 "Nombre de pas filtrés"
259 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
260 return __indexOfFilteredItems
263 def steplist(self, **kwargs):
264 "Nombre de pas filtrés"
265 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
266 return list(__indexOfFilteredItems)
268 # ---------------------------------------------------------
271 Renvoie la série, contenant à chaque pas, la valeur moyenne des données
272 au pas. Il faut que le type de base soit compatible avec les types
276 return [numpy.matrix(item).mean() for item in self.__values]
278 raise TypeError("Base type is incompatible with numpy")
280 def stds(self, ddof=0):
282 Renvoie la série, contenant à chaque pas, l'écart-type des données
283 au pas. Il faut que le type de base soit compatible avec les types
286 ddof : c'est le nombre de degrés de liberté pour le calcul de
287 l'écart-type, qui est dans le diviseur. Inutile avant Numpy 1.1
290 if numpy.version.version >= '1.1.0':
291 return [numpy.matrix(item).std(ddof=ddof) for item in self.__values]
293 return [numpy.matrix(item).std() for item in self.__values]
295 raise TypeError("Base type is incompatible with numpy")
299 Renvoie la série, contenant à chaque pas, la somme des données au pas.
300 Il faut que le type de base soit compatible avec les types élémentaires
304 return [numpy.matrix(item).sum() for item in self.__values]
306 raise TypeError("Base type is incompatible with numpy")
310 Renvoie la série, contenant à chaque pas, le minimum des données au pas.
311 Il faut que le type de base soit compatible avec les types élémentaires
315 return [numpy.matrix(item).min() for item in self.__values]
317 raise TypeError("Base type is incompatible with numpy")
321 Renvoie la série, contenant à chaque pas, la maximum des données au pas.
322 Il faut que le type de base soit compatible avec les types élémentaires
326 return [numpy.matrix(item).max() for item in self.__values]
328 raise TypeError("Base type is incompatible with numpy")
335 geometry = "600x400",
339 "Préparation des plots"
341 # Vérification de la disponibilité du module Gnuplot
344 self.__gnuplot = 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 self.__gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -persist -geometry '+geometry
352 self.__gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -geometry '+geometry
355 self.__g = self.__gnuplot.Gnuplot() # persist=1
356 self.__g('set terminal '+self.__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
416 if not self.__dynamic:
417 self.__preplots(title, xlabel, ylabel, ltitle, geometry, persist, pause )
419 self.__dynamic = True
420 if len(self.__values) == 0: return 0
422 # Tracé du ou des vecteurs demandés
424 if step is not None and step < len(self.__values):
426 elif item is not None and item < len(self.__values):
429 indexes = indexes + list(range(len(self.__values)))
432 for index in indexes:
433 self.__g('set title "'+str(title)+' (pas '+str(index)+')"')
434 if isinstance(steps,list) or isinstance(steps,numpy.ndarray):
437 Steps = list(range(len(self.__values[index])))
439 self.__g.plot( self.__gnuplot.Data( Steps, self.__values[index], title=ltitle ) )
443 stepfilename = "%s_%03i.ps"%(filename,i)
444 if os.path.isfile(stepfilename):
445 raise ValueError("Error: a file with this name \"%s\" already exists."%stepfilename)
446 self.__g.hardcopy(filename=stepfilename, color=1)
448 eval(input('Please press return to continue...\n'))
452 Affichage dans le cas du suivi dynamique de la variable
454 if self.__dynamic and len(self.__values) < 2: return 0
456 self.__g('set title "'+str(self.__title))
457 Steps = list(range(len(self.__values)))
458 self.__g.plot( self.__gnuplot.Data( Steps, self.__values, title=self.__ltitle ) )
461 eval(input('Please press return to continue...\n'))
463 # ---------------------------------------------------------
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")
539 # On pourrait aussi utiliser les autres attributs d'une "matrix", comme
548 geometry = "600x400",
554 Renvoie un affichage unique pour l'ensemble des valeurs à chaque pas, si
555 elles sont compatibles avec un affichage Gnuplot (donc essentiellement
556 un vecteur). Si l'argument "step" existe dans la liste des pas de
557 stockage effectués, renvoie l'affichage de la valeur stockée à ce pas
558 "step". Si l'argument "item" est correct, renvoie l'affichage de la
559 valeur stockée au numéro "item".
562 - steps : liste unique des pas de l'axe des X, ou None si c'est
563 la numérotation par défaut
564 - title : base du titre général, qui sera automatiquement
565 complétée par la mention du pas
566 - xlabel : label de l'axe des X
567 - ylabel : label de l'axe des Y
568 - ltitle : titre associé au vecteur tracé
569 - geometry : taille en pixels de la fenêtre et position du coin haut
570 gauche, au format X11 : LxH+X+Y (défaut : 600x400)
571 - filename : nom de fichier Postscript pour une sauvegarde
572 - persist : booléen indiquant que la fenêtre affichée sera
573 conservée lors du passage au dessin suivant
574 Par défaut, persist = False
575 - pause : booléen indiquant une pause après chaque tracé, et
577 Par défaut, pause = True
580 # Vérification de la disponibilité du module Gnuplot
583 self.__gnuplot = Gnuplot
585 raise ImportError("The Gnuplot module is required to plot the object.")
587 # Vérification et compléments sur les paramètres d'entrée
589 self.__gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -persist -geometry '+geometry
591 self.__gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -geometry '+geometry
594 if isinstance(steps,list) or isinstance(steps, numpy.ndarray):
597 Steps = list(range(len(self.__values[0])))
598 self.__g = self.__gnuplot.Gnuplot() # persist=1
599 self.__g('set terminal '+self.__gnuplot.GnuplotOpts.default_term)
600 self.__g('set style data lines')
602 self.__g('set autoscale')
603 self.__g('set title "'+str(title) +'"')
604 self.__g('set xlabel "'+str(xlabel)+'"')
605 self.__g('set ylabel "'+str(ylabel)+'"')
607 # Tracé du ou des vecteurs demandés
608 indexes = list(range(len(self.__values)))
609 self.__g.plot( self.__gnuplot.Data( Steps, self.__values[indexes.pop(0)], title=ltitle+" (pas 0)" ) )
610 for index in indexes:
611 self.__g.replot( self.__gnuplot.Data( Steps, self.__values[index], title=ltitle+" (pas %i)"%index ) )
614 self.__g.hardcopy(filename=filename, color=1)
616 eval(input('Please press return to continue...\n'))
618 # ---------------------------------------------------------
619 def setDataObserver(self, HookFunction = None, HookParameters = None, Scheduler = None):
621 Association à la variable d'un triplet définissant un observer
623 Le Scheduler attendu est une fréquence, une simple liste d'index ou un
627 # Vérification du Scheduler
628 # -------------------------
630 if isinstance(Scheduler,int): # Considéré comme une fréquence à partir de 0
631 Schedulers = range( 0, maxiter, int(Scheduler) )
632 elif isinstance(Scheduler,range): # Considéré comme un itérateur
633 Schedulers = Scheduler
634 elif isinstance(Scheduler,(list,tuple)): # Considéré comme des index explicites
635 Schedulers = [iLong(i) for i in Scheduler] # map( long, Scheduler )
636 else: # Dans tous les autres cas, activé par défaut
637 Schedulers = range( 0, maxiter )
639 # Stockage interne de l'observer dans la variable
640 # -----------------------------------------------
641 self.__dataobservers.append( [HookFunction, HookParameters, Schedulers] )
643 def removeDataObserver(self, HookFunction = None, AllObservers = False):
645 Suppression d'un observer nommé sur la variable.
647 On peut donner dans HookFunction la meme fonction que lors de la
648 définition, ou un simple string qui est le nom de la fonction. Si
649 AllObservers est vrai, supprime tous les observers enregistrés.
651 if hasattr(HookFunction,"func_name"):
652 name = str( HookFunction.func_name )
653 elif hasattr(HookFunction,"__name__"):
654 name = str( HookFunction.__name__ )
655 elif isinstance(HookFunction,str):
656 name = str( HookFunction )
662 for [hf, hp, hs] in self.__dataobservers:
664 if name is hf.__name__ or AllObservers: index_to_remove.append( i )
665 index_to_remove.reverse()
666 for i in index_to_remove:
667 self.__dataobservers.pop( i )
668 return len(index_to_remove)
670 def hasDataObserver(self):
671 return bool(len(self.__dataobservers) > 0)
673 # ==============================================================================
674 class SchedulerTrigger(object):
676 Classe générale d'interface de type Scheduler/Trigger
679 simplifiedCombo = None,
681 endTime = int( 1e9 ),
688 # ==============================================================================
689 class OneScalar(Persistence):
691 Classe définissant le stockage d'une valeur unique réelle (float) par pas.
693 Le type de base peut être changé par la méthode "basetype", mais il faut que
694 le nouveau type de base soit compatible avec les types par éléments de
695 numpy. On peut même utiliser cette classe pour stocker des vecteurs/listes
696 ou des matrices comme dans les classes suivantes, mais c'est déconseillé
697 pour conserver une signification claire des noms.
699 def __init__(self, name="", unit="", basetype = float):
700 Persistence.__init__(self, name, unit, basetype)
702 class OneIndex(Persistence):
704 Classe définissant le stockage d'une valeur unique entière (int) par pas.
706 def __init__(self, name="", unit="", basetype = int):
707 Persistence.__init__(self, name, unit, basetype)
709 class OneVector(Persistence):
711 Classe de stockage d'une liste de valeurs numériques homogènes par pas. Ne
712 pas utiliser cette classe pour des données hétérogènes, mais "OneList".
714 def __init__(self, name="", unit="", basetype = numpy.ravel):
715 Persistence.__init__(self, name, unit, basetype)
717 class OneMatrix(Persistence):
719 Classe de stockage d'une matrice de valeurs (numpy.matrix) par pas.
721 def __init__(self, name="", unit="", basetype = numpy.matrix):
722 Persistence.__init__(self, name, unit, basetype)
724 class OneList(Persistence):
726 Classe de stockage d'une liste de valeurs hétérogènes (list) par pas. Ne pas
727 utiliser cette classe pour des données numériques homogènes, mais
730 def __init__(self, name="", unit="", basetype = list):
731 Persistence.__init__(self, name, unit, basetype)
734 "Fonction transparente, sans effet sur son argument"
737 class OneNoType(Persistence):
739 Classe de stockage d'un objet sans modification (cast) de type. Attention,
740 selon le véritable type de l'objet stocké à chaque pas, les opérations
741 arithmétiques à base de numpy peuvent être invalides ou donner des résultats
742 inattendus. Cette classe n'est donc à utiliser qu'à bon escient
743 volontairement, et pas du tout par défaut.
745 def __init__(self, name="", unit="", basetype = NoType):
746 Persistence.__init__(self, name, unit, basetype)
748 # ==============================================================================
749 class CompositePersistence(object):
751 Structure de stockage permettant de rassembler plusieurs objets de
754 Des objets par défaut sont prévus, et des objets supplémentaires peuvent
757 def __init__(self, name="", defaults=True):
761 La gestion interne des données est exclusivement basée sur les variables
762 initialisées ici (qui ne sont pas accessibles depuis l'extérieur des
763 objets comme des attributs) :
764 __StoredObjects : objets de type persistence collectés dans cet objet
766 self.__name = str(name)
768 self.__StoredObjects = {}
770 # Definition des objets par defaut
771 # --------------------------------
773 self.__StoredObjects["Informations"] = OneNoType("Informations")
774 self.__StoredObjects["Background"] = OneVector("Background", basetype=numpy.array)
775 self.__StoredObjects["BackgroundError"] = OneMatrix("BackgroundError")
776 self.__StoredObjects["Observation"] = OneVector("Observation", basetype=numpy.array)
777 self.__StoredObjects["ObservationError"] = OneMatrix("ObservationError")
778 self.__StoredObjects["Analysis"] = OneVector("Analysis", basetype=numpy.array)
779 self.__StoredObjects["AnalysisError"] = OneMatrix("AnalysisError")
780 self.__StoredObjects["Innovation"] = OneVector("Innovation", basetype=numpy.array)
781 self.__StoredObjects["KalmanGainK"] = OneMatrix("KalmanGainK")
782 self.__StoredObjects["OperatorH"] = OneMatrix("OperatorH")
783 self.__StoredObjects["RmsOMA"] = OneScalar("RmsOMA")
784 self.__StoredObjects["RmsOMB"] = OneScalar("RmsOMB")
785 self.__StoredObjects["RmsBMA"] = OneScalar("RmsBMA")
788 def store(self, name=None, value=None, **kwargs):
790 Stockage d'une valeur "value" pour le "step" dans la variable "name".
792 if name is None: raise ValueError("Storable object name is required for storage.")
793 if name not in self.__StoredObjects.keys():
794 raise ValueError("No such name '%s' exists in storable objects."%name)
795 self.__StoredObjects[name].store( value=value, **kwargs )
797 def add_object(self, name=None, persistenceType=Persistence, basetype=None ):
799 Ajoute dans les objets stockables un nouvel objet défini par son nom, son
800 type de Persistence et son type de base à chaque pas.
802 if name is None: raise ValueError("Object name is required for adding an object.")
803 if name in self.__StoredObjects.keys():
804 raise ValueError("An object with the same name '%s' already exists in storable objects. Choose another one."%name)
806 self.__StoredObjects[name] = persistenceType( name=str(name) )
808 self.__StoredObjects[name] = persistenceType( name=str(name), basetype=basetype )
810 def get_object(self, name=None ):
812 Renvoie l'objet de type Persistence qui porte le nom demandé.
814 if name is None: raise ValueError("Object name is required for retrieving an object.")
815 if name not in self.__StoredObjects.keys():
816 raise ValueError("No such name '%s' exists in stored objects."%name)
817 return self.__StoredObjects[name]
819 def set_object(self, name=None, objet=None ):
821 Affecte directement un 'objet' qui porte le nom 'name' demandé.
822 Attention, il n'est pas effectué de vérification sur le type, qui doit
823 comporter les méthodes habituelles de Persistence pour que cela
826 if name is None: raise ValueError("Object name is required for setting an object.")
827 if name in self.__StoredObjects.keys():
828 raise ValueError("An object with the same name '%s' already exists in storable objects. Choose another one."%name)
829 self.__StoredObjects[name] = objet
831 def del_object(self, name=None ):
833 Supprime un objet de la liste des objets stockables.
835 if name is None: raise ValueError("Object name is required for retrieving an object.")
836 if name not in self.__StoredObjects.keys():
837 raise ValueError("No such name '%s' exists in stored objects."%name)
838 del self.__StoredObjects[name]
840 # ---------------------------------------------------------
841 # Méthodes d'accès de type dictionnaire
842 def __getitem__(self, name=None ):
843 "x.__getitem__(y) <==> x[y]"
844 return self.get_object( name )
846 def __setitem__(self, name=None, objet=None ):
847 "x.__setitem__(i, y) <==> x[i]=y"
848 self.set_object( name, objet )
851 "D.keys() -> list of D's keys"
852 return self.get_stored_objects(hideVoidObjects = False)
855 "D.values() -> list of D's values"
856 return self.__StoredObjects.values()
859 "D.items() -> list of D's (key, value) pairs, as 2-tuples"
860 return self.__StoredObjects.items()
862 # ---------------------------------------------------------
863 def get_stored_objects(self, hideVoidObjects = False):
864 "Renvoie la liste des objets présents"
865 objs = self.__StoredObjects.keys()
870 if len(self.__StoredObjects[k]) > 0: usedObjs.append( k )
877 # ---------------------------------------------------------
878 def save_composite(self, filename=None, mode="pickle", compress="gzip"):
880 Enregistre l'objet dans le fichier indiqué selon le "mode" demandé,
881 et renvoi le nom du fichier
885 if compress == "gzip":
886 filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl.gz"
887 elif compress == "bzip2":
888 filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl.bz2"
890 filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl"
892 filename = os.path.abspath( filename )
895 if compress == "gzip":
897 output = gzip.open( filename, 'wb')
898 elif compress == "bzip2":
900 output = bz2.BZ2File( filename, 'wb')
902 output = open( filename, 'wb')
903 pickle.dump(self, output)
906 raise ValueError("Save mode '%s' unknown. Choose another one."%mode)
910 def load_composite(self, filename=None, mode="pickle", compress="gzip"):
912 Recharge un objet composite sauvé en fichier
916 raise ValueError("A file name if requested to load a composite.")
918 filename = os.path.abspath( filename )
921 if compress == "gzip":
923 pkl_file = gzip.open( filename, 'rb')
924 elif compress == "bzip2":
926 pkl_file = bz2.BZ2File( filename, 'rb')
928 pkl_file = open(filename, 'rb')
929 output = pickle.load(pkl_file)
930 for k in output.keys():
933 raise ValueError("Load mode '%s' unknown. Choose another one."%mode)
937 # ==============================================================================
938 if __name__ == "__main__":
939 print('\n AUTODIAGNOSTIC \n')