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)
150 def __getitem__(self, index=None ):
151 "x.__getitem__(y) <==> x[y]"
152 return copy.copy(self.__values[index])
154 def count(self, value):
155 "L.count(value) -> integer -- return number of occurrences of value"
156 return self.__values.count(value)
158 def index(self, value, start=0, stop=None):
159 "L.index(value, [start, [stop]]) -> integer -- return first index of value."
160 if stop is None : stop = len(self.__values)
161 return self.__values.index(value, start, stop)
163 # ---------------------------------------------------------
164 def __filteredIndexes(self, **kwargs):
165 "Function interne filtrant les index"
166 __indexOfFilteredItems = range(len(self.__tags))
167 __filteringKwTags = kwargs.keys()
168 if len(__filteringKwTags) > 0:
169 for tagKey in __filteringKwTags:
171 for i in __indexOfFilteredItems:
172 if tagKey in self.__tags[i]:
173 if self.__tags[i][tagKey] == kwargs[tagKey]:
175 elif isinstance(kwargs[tagKey],(list,tuple)) and self.__tags[i][tagKey] in kwargs[tagKey]:
177 __indexOfFilteredItems = __tmp
178 if len(__indexOfFilteredItems) == 0: break
179 return __indexOfFilteredItems
181 # ---------------------------------------------------------
182 def values(self, **kwargs):
183 "D.values() -> list of D's values"
184 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
185 return [self.__values[i] for i in __indexOfFilteredItems]
187 def keys(self, keyword=None , **kwargs):
188 "D.keys() -> list of D's keys"
189 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
191 for i in __indexOfFilteredItems:
192 if keyword in self.__tags[i]:
193 __keys.append( self.__tags[i][keyword] )
195 __keys.append( None )
198 def items(self, keyword=None , **kwargs):
199 "D.items() -> list of D's (key, value) pairs, as 2-tuples"
200 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
202 for i in __indexOfFilteredItems:
203 if keyword in self.__tags[i]:
204 __pairs.append( (self.__tags[i][keyword], self.__values[i]) )
206 __pairs.append( (None, self.__values[i]) )
210 "D.tagkeys() -> list of D's tag keys"
212 for dicotags in self.__tags:
213 __allKeys.extend( list(dicotags.keys()) )
214 __allKeys = sorted(set(__allKeys))
217 # def valueserie(self, item=None, allSteps=True, **kwargs):
218 # if item is not None:
219 # return self.__values[item]
221 # __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
222 # if not allSteps and len(__indexOfFilteredItems) > 0:
223 # return self.__values[__indexOfFilteredItems[0]]
225 # return [self.__values[i] for i in __indexOfFilteredItems]
227 def tagserie(self, item=None, withValues=False, outputTag=None, **kwargs):
228 "D.tagserie() -> list of D's tag serie"
230 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
232 __indexOfFilteredItems = [item,]
234 # Dans le cas où la sortie donne les valeurs d'un "outputTag"
235 if outputTag is not None and isinstance(outputTag,str) :
237 for index in __indexOfFilteredItems:
238 if outputTag in self.__tags[index].keys():
239 outputValues.append( self.__tags[index][outputTag] )
240 outputValues = sorted(set(outputValues))
243 # Dans le cas où la sortie donne les tags satisfaisants aux conditions
246 return [self.__tags[index] for index in __indexOfFilteredItems]
249 for index in __indexOfFilteredItems:
250 allTags.update( self.__tags[index] )
251 allKeys = sorted(allTags.keys())
254 # ---------------------------------------------------------
256 def stepnumber(self):
258 return len(self.__values)
261 def stepserie(self, **kwargs):
262 "Nombre de pas filtrés"
263 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
264 return __indexOfFilteredItems
267 def steplist(self, **kwargs):
268 "Nombre de pas filtrés"
269 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
270 return list(__indexOfFilteredItems)
272 # ---------------------------------------------------------
275 Renvoie la série, contenant à chaque pas, la valeur moyenne des données
276 au pas. Il faut que le type de base soit compatible avec les types
280 return [numpy.mean(item, dtype=mfp) for item in self.__values]
282 raise TypeError("Base type is incompatible with numpy")
284 def stds(self, ddof=0):
286 Renvoie la série, contenant à chaque pas, l'écart-type des données
287 au pas. Il faut que le type de base soit compatible avec les types
290 ddof : c'est le nombre de degrés de liberté pour le calcul de
291 l'écart-type, qui est dans le diviseur. Inutile avant Numpy 1.1
294 if numpy.version.version >= '1.1.0':
295 return [numpy.array(item).std(ddof=ddof) for item in self.__values]
297 return [numpy.array(item).std() for item in self.__values]
299 raise TypeError("Base type is incompatible with numpy")
303 Renvoie la série, contenant à chaque pas, la somme des données au pas.
304 Il faut que le type de base soit compatible avec les types élémentaires
308 return [numpy.array(item).sum() for item in self.__values]
310 raise TypeError("Base type is incompatible with numpy")
314 Renvoie la série, contenant à chaque pas, le minimum des données au pas.
315 Il faut que le type de base soit compatible avec les types élémentaires
319 return [numpy.array(item).min() for item in self.__values]
321 raise TypeError("Base type is incompatible with numpy")
325 Renvoie la série, contenant à chaque pas, la maximum des données au pas.
326 Il faut que le type de base soit compatible avec les types élémentaires
330 return [numpy.array(item).max() for item in self.__values]
332 raise TypeError("Base type is incompatible with numpy")
339 geometry = "600x400",
343 "Préparation des plots"
345 # Vérification de la disponibilité du module Gnuplot
347 raise ImportError("The Gnuplot module is required to plot the object.")
349 # Vérification et compléments sur les paramètres d'entrée
351 Gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -persist -geometry '+geometry
353 Gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -geometry '+geometry
356 self.__g = Gnuplot.Gnuplot() # persist=1
357 self.__g('set terminal '+Gnuplot.GnuplotOpts.default_term)
358 self.__g('set style data lines')
360 self.__g('set autoscale')
361 self.__g('set xlabel "'+str(xlabel)+'"')
362 self.__g('set ylabel "'+str(ylabel)+'"')
364 self.__ltitle = ltitle
375 geometry = "600x400",
382 Renvoie un affichage de la valeur à chaque pas, si elle est compatible
383 avec un affichage Gnuplot (donc essentiellement un vecteur). Si
384 l'argument "step" existe dans la liste des pas de stockage effectués,
385 renvoie l'affichage de la valeur stockée à ce pas "step". Si l'argument
386 "item" est correct, renvoie l'affichage de la valeur stockée au numéro
387 "item". Par défaut ou en l'absence de "step" ou "item", renvoie un
388 affichage successif de tous les pas.
391 - step : valeur du pas à afficher
392 - item : index de la valeur à afficher
393 - steps : liste unique des pas de l'axe des X, ou None si c'est
394 la numérotation par défaut
395 - title : base du titre général, qui sera automatiquement
396 complétée par la mention du pas
397 - xlabel : label de l'axe des X
398 - ylabel : label de l'axe des Y
399 - ltitle : titre associé au vecteur tracé
400 - geometry : taille en pixels de la fenêtre et position du coin haut
401 gauche, au format X11 : LxH+X+Y (défaut : 600x400)
402 - filename : base de nom de fichier Postscript pour une sauvegarde,
403 qui est automatiquement complétée par le numéro du
404 fichier calculé par incrément simple de compteur
405 - dynamic : effectue un affichage des valeurs à chaque stockage
406 (au-delà du second). La méthode "plots" permet de
407 déclarer l'affichage dynamique, et c'est la méthode
408 "__replots" qui est utilisée pour l'effectuer
409 - persist : booléen indiquant que la fenêtre affichée sera
410 conservée lors du passage au dessin suivant
411 Par défaut, persist = False
412 - pause : booléen indiquant une pause après chaque tracé, et
414 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( 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( Gnuplot.Data( Steps, self.__values, title=self.__ltitle ) )
461 eval(input('Please press return to continue...\n'))
463 # ---------------------------------------------------------
464 # On pourrait aussi utiliser d'autres attributs d'un "array" comme "tofile"
467 Renvoie la moyenne sur toutes les valeurs sans tenir compte de la
468 longueur des pas. Il faut que le type de base soit compatible avec
469 les types élémentaires numpy.
472 if self.__basetype in [int, float]:
473 return float( numpy.mean(self.__values, dtype=mfp) )
475 return numpy.mean(self.__values, axis=0, dtype=mfp)
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)
492 return numpy.array(self.__values).std(axis=0)
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')