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 OneScalar(Persistence):
676 Classe définissant le stockage d'une valeur unique réelle (float) par pas.
678 Le type de base peut être changé par la méthode "basetype", mais il faut que
679 le nouveau type de base soit compatible avec les types par éléments de
680 numpy. On peut même utiliser cette classe pour stocker des vecteurs/listes
681 ou des matrices comme dans les classes suivantes, mais c'est déconseillé
682 pour conserver une signification claire des noms.
684 def __init__(self, name="", unit="", basetype = float):
685 Persistence.__init__(self, name, unit, basetype)
687 class OneIndex(Persistence):
689 Classe définissant le stockage d'une valeur unique entière (int) par pas.
691 def __init__(self, name="", unit="", basetype = int):
692 Persistence.__init__(self, name, unit, basetype)
694 class OneVector(Persistence):
696 Classe de stockage d'une liste de valeurs numériques homogènes par pas. Ne
697 pas utiliser cette classe pour des données hétérogènes, mais "OneList".
699 def __init__(self, name="", unit="", basetype = numpy.ravel):
700 Persistence.__init__(self, name, unit, basetype)
702 class OneMatrix(Persistence):
704 Classe de stockage d'une matrice de valeurs (numpy.matrix) par pas.
706 def __init__(self, name="", unit="", basetype = numpy.matrix):
707 Persistence.__init__(self, name, unit, basetype)
709 class OneList(Persistence):
711 Classe de stockage d'une liste de valeurs hétérogènes (list) par pas. Ne pas
712 utiliser cette classe pour des données numériques homogènes, mais
715 def __init__(self, name="", unit="", basetype = list):
716 Persistence.__init__(self, name, unit, basetype)
719 "Fonction transparente, sans effet sur son argument"
722 class OneNoType(Persistence):
724 Classe de stockage d'un objet sans modification (cast) de type. Attention,
725 selon le véritable type de l'objet stocké à chaque pas, les opérations
726 arithmétiques à base de numpy peuvent être invalides ou donner des résultats
727 inattendus. Cette classe n'est donc à utiliser qu'à bon escient
728 volontairement, et pas du tout par défaut.
730 def __init__(self, name="", unit="", basetype = NoType):
731 Persistence.__init__(self, name, unit, basetype)
733 # ==============================================================================
734 class CompositePersistence(object):
736 Structure de stockage permettant de rassembler plusieurs objets de
739 Des objets par défaut sont prévus, et des objets supplémentaires peuvent
742 def __init__(self, name="", defaults=True):
746 La gestion interne des données est exclusivement basée sur les variables
747 initialisées ici (qui ne sont pas accessibles depuis l'extérieur des
748 objets comme des attributs) :
749 __StoredObjects : objets de type persistence collectés dans cet objet
751 self.__name = str(name)
753 self.__StoredObjects = {}
755 # Definition des objets par defaut
756 # --------------------------------
758 self.__StoredObjects["Informations"] = OneNoType("Informations")
759 self.__StoredObjects["Background"] = OneVector("Background", basetype=numpy.array)
760 self.__StoredObjects["BackgroundError"] = OneMatrix("BackgroundError")
761 self.__StoredObjects["Observation"] = OneVector("Observation", basetype=numpy.array)
762 self.__StoredObjects["ObservationError"] = OneMatrix("ObservationError")
763 self.__StoredObjects["Analysis"] = OneVector("Analysis", basetype=numpy.array)
764 self.__StoredObjects["AnalysisError"] = OneMatrix("AnalysisError")
765 self.__StoredObjects["Innovation"] = OneVector("Innovation", basetype=numpy.array)
766 self.__StoredObjects["KalmanGainK"] = OneMatrix("KalmanGainK")
767 self.__StoredObjects["OperatorH"] = OneMatrix("OperatorH")
768 self.__StoredObjects["RmsOMA"] = OneScalar("RmsOMA")
769 self.__StoredObjects["RmsOMB"] = OneScalar("RmsOMB")
770 self.__StoredObjects["RmsBMA"] = OneScalar("RmsBMA")
773 def store(self, name=None, value=None, **kwargs):
775 Stockage d'une valeur "value" pour le "step" dans la variable "name".
777 if name is None: raise ValueError("Storable object name is required for storage.")
778 if name not in self.__StoredObjects.keys():
779 raise ValueError("No such name '%s' exists in storable objects."%name)
780 self.__StoredObjects[name].store( value=value, **kwargs )
782 def add_object(self, name=None, persistenceType=Persistence, basetype=None ):
784 Ajoute dans les objets stockables un nouvel objet défini par son nom, son
785 type de Persistence et son type de base à chaque pas.
787 if name is None: raise ValueError("Object name is required for adding an object.")
788 if name in self.__StoredObjects.keys():
789 raise ValueError("An object with the same name '%s' already exists in storable objects. Choose another one."%name)
791 self.__StoredObjects[name] = persistenceType( name=str(name) )
793 self.__StoredObjects[name] = persistenceType( name=str(name), basetype=basetype )
795 def get_object(self, name=None ):
797 Renvoie l'objet de type Persistence qui porte le nom demandé.
799 if name is None: raise ValueError("Object name is required for retrieving an object.")
800 if name not in self.__StoredObjects.keys():
801 raise ValueError("No such name '%s' exists in stored objects."%name)
802 return self.__StoredObjects[name]
804 def set_object(self, name=None, objet=None ):
806 Affecte directement un 'objet' qui porte le nom 'name' demandé.
807 Attention, il n'est pas effectué de vérification sur le type, qui doit
808 comporter les méthodes habituelles de Persistence pour que cela
811 if name is None: raise ValueError("Object name is required for setting an object.")
812 if name in self.__StoredObjects.keys():
813 raise ValueError("An object with the same name '%s' already exists in storable objects. Choose another one."%name)
814 self.__StoredObjects[name] = objet
816 def del_object(self, name=None ):
818 Supprime un objet de la liste des objets stockables.
820 if name is None: raise ValueError("Object name is required for retrieving an object.")
821 if name not in self.__StoredObjects.keys():
822 raise ValueError("No such name '%s' exists in stored objects."%name)
823 del self.__StoredObjects[name]
825 # ---------------------------------------------------------
826 # Méthodes d'accès de type dictionnaire
827 def __getitem__(self, name=None ):
828 "x.__getitem__(y) <==> x[y]"
829 return self.get_object( name )
831 def __setitem__(self, name=None, objet=None ):
832 "x.__setitem__(i, y) <==> x[i]=y"
833 self.set_object( name, objet )
836 "D.keys() -> list of D's keys"
837 return self.get_stored_objects(hideVoidObjects = False)
840 "D.values() -> list of D's values"
841 return self.__StoredObjects.values()
844 "D.items() -> list of D's (key, value) pairs, as 2-tuples"
845 return self.__StoredObjects.items()
847 # ---------------------------------------------------------
848 def get_stored_objects(self, hideVoidObjects = False):
849 "Renvoie la liste des objets présents"
850 objs = self.__StoredObjects.keys()
855 if len(self.__StoredObjects[k]) > 0: usedObjs.append( k )
862 # ---------------------------------------------------------
863 def save_composite(self, filename=None, mode="pickle", compress="gzip"):
865 Enregistre l'objet dans le fichier indiqué selon le "mode" demandé,
866 et renvoi le nom du fichier
870 if compress == "gzip":
871 filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl.gz"
872 elif compress == "bzip2":
873 filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl.bz2"
875 filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl"
877 filename = os.path.abspath( filename )
880 if compress == "gzip":
882 output = gzip.open( filename, 'wb')
883 elif compress == "bzip2":
885 output = bz2.BZ2File( filename, 'wb')
887 output = open( filename, 'wb')
888 pickle.dump(self, output)
891 raise ValueError("Save mode '%s' unknown. Choose another one."%mode)
895 def load_composite(self, filename=None, mode="pickle", compress="gzip"):
897 Recharge un objet composite sauvé en fichier
901 raise ValueError("A file name if requested to load a composite.")
903 filename = os.path.abspath( filename )
906 if compress == "gzip":
908 pkl_file = gzip.open( filename, 'rb')
909 elif compress == "bzip2":
911 pkl_file = bz2.BZ2File( filename, 'rb')
913 pkl_file = open(filename, 'rb')
914 output = pickle.load(pkl_file)
915 for k in output.keys():
918 raise ValueError("Load mode '%s' unknown. Choose another one."%mode)
922 # ==============================================================================
923 if __name__ == "__main__":
924 print('\n AUTODIAGNOSTIC \n')