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 os, sys, numpy, copy
33 from daCore.PlatformInfo import PathManagement ; PathManagement()
34 from daCore.PlatformInfo import has_gnuplot
38 if sys.version_info.major < 3:
41 import cPickle as pickle
46 # ==============================================================================
47 class Persistence(object):
49 Classe générale de persistence définissant les accesseurs nécessaires
52 def __init__(self, name="", unit="", basetype=str):
56 basetype : type de base de l'objet stocké à chaque pas
58 La gestion interne des données est exclusivement basée sur les variables
59 initialisées ici (qui ne sont pas accessibles depuis l'extérieur des
60 objets comme des attributs) :
61 __basetype : le type de base de chaque valeur, sous la forme d'un type
62 permettant l'instanciation ou le casting Python
63 __values : les valeurs de stockage. Par défaut, c'est None
65 self.__name = str(name)
66 self.__unit = str(unit)
68 self.__basetype = basetype
73 self.__dynamic = False
79 self.__dataobservers = []
81 def basetype(self, basetype=None):
83 Renvoie ou met en place le type de base des objets stockés
86 return self.__basetype
88 self.__basetype = basetype
90 def store(self, value=None, **kwargs):
92 Stocke une valeur avec ses informations de filtrage.
94 if value is None: raise ValueError("Value argument required")
96 self.__values.append(copy.copy(self.__basetype(value)))
97 self.__tags.append(kwargs)
99 if self.__dynamic: self.__replots()
100 __step = len(self.__values) - 1
101 for hook, parameters, scheduler in self.__dataobservers:
102 if __step in scheduler:
103 hook( self, parameters )
105 def pop(self, item=None):
107 Retire une valeur enregistree par son index de stockage. Sans argument,
108 retire le dernier objet enregistre.
112 self.__values.pop(__index)
113 self.__tags.pop(__index)
120 Renvoie la taille sous forme numpy du dernier objet stocké. Si c'est un
121 objet numpy, renvoie le shape. Si c'est un entier, un flottant, un
122 complexe, renvoie 1. Si c'est une liste ou un dictionnaire, renvoie la
123 longueur. Par défaut, renvoie 1.
125 if len(self.__values) > 0:
126 if self.__basetype in [numpy.matrix, numpy.ndarray, numpy.array, numpy.ravel]:
127 return self.__values[-1].shape
128 elif self.__basetype in [int, float]:
130 elif self.__basetype in [list, dict]:
131 return (len(self.__values[-1]),)
135 raise ValueError("Object has no shape before its first storage")
137 # ---------------------------------------------------------
139 "x.__str__() <==> str(x)"
140 msg = " Index Value Tags\n"
141 for i,v in enumerate(self.__values):
142 msg += " i=%05i %10s %s\n"%(i,v,self.__tags[i])
146 "x.__len__() <==> len(x)"
147 return len(self.__values)
149 def __getitem__(self, index=None ):
150 "x.__getitem__(y) <==> x[y]"
151 return copy.copy(self.__values[index])
153 def count(self, value):
154 "L.count(value) -> integer -- return number of occurrences of value"
155 return self.__values.count(value)
157 def index(self, value, start=0, stop=None):
158 "L.index(value, [start, [stop]]) -> integer -- return first index of value."
159 if stop is None : stop = len(self.__values)
160 return self.__values.index(value, start, stop)
162 # ---------------------------------------------------------
163 def __filteredIndexes(self, **kwargs):
164 "Function interne filtrant les index"
165 __indexOfFilteredItems = range(len(self.__tags))
166 __filteringKwTags = kwargs.keys()
167 if len(__filteringKwTags) > 0:
168 for tagKey in __filteringKwTags:
170 for i in __indexOfFilteredItems:
171 if tagKey in self.__tags[i]:
172 if self.__tags[i][tagKey] == kwargs[tagKey]:
174 elif isinstance(kwargs[tagKey],(list,tuple)) and self.__tags[i][tagKey] in kwargs[tagKey]:
176 __indexOfFilteredItems = __tmp
177 if len(__indexOfFilteredItems) == 0: break
178 return __indexOfFilteredItems
180 # ---------------------------------------------------------
181 def values(self, **kwargs):
182 "D.values() -> list of D's values"
183 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
184 return [self.__values[i] for i in __indexOfFilteredItems]
186 def keys(self, keyword=None , **kwargs):
187 "D.keys() -> list of D's keys"
188 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
190 for i in __indexOfFilteredItems:
191 if keyword in self.__tags[i]:
192 __keys.append( self.__tags[i][keyword] )
194 __keys.append( None )
197 def items(self, keyword=None , **kwargs):
198 "D.items() -> list of D's (key, value) pairs, as 2-tuples"
199 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
201 for i in __indexOfFilteredItems:
202 if keyword in self.__tags[i]:
203 __pairs.append( (self.__tags[i][keyword], self.__values[i]) )
205 __pairs.append( (None, self.__values[i]) )
209 "D.tagkeys() -> list of D's tag keys"
211 for dicotags in self.__tags:
212 __allKeys.extend( list(dicotags.keys()) )
213 __allKeys = sorted(set(__allKeys))
216 # def valueserie(self, item=None, allSteps=True, **kwargs):
217 # if item is not None:
218 # return self.__values[item]
220 # __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
221 # if not allSteps and len(__indexOfFilteredItems) > 0:
222 # return self.__values[__indexOfFilteredItems[0]]
224 # return [self.__values[i] for i in __indexOfFilteredItems]
226 def tagserie(self, item=None, withValues=False, outputTag=None, **kwargs):
227 "D.tagserie() -> list of D's tag serie"
229 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
231 __indexOfFilteredItems = [item,]
233 # Dans le cas où la sortie donne les valeurs d'un "outputTag"
234 if outputTag is not None and isinstance(outputTag,str) :
236 for index in __indexOfFilteredItems:
237 if outputTag in self.__tags[index].keys():
238 outputValues.append( self.__tags[index][outputTag] )
239 outputValues = sorted(set(outputValues))
242 # Dans le cas où la sortie donne les tags satisfaisants aux conditions
245 return [self.__tags[index] for index in __indexOfFilteredItems]
248 for index in __indexOfFilteredItems:
249 allTags.update( self.__tags[index] )
250 allKeys = sorted(allTags.keys())
253 # ---------------------------------------------------------
255 def stepnumber(self):
257 return len(self.__values)
260 def stepserie(self, **kwargs):
261 "Nombre de pas filtrés"
262 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
263 return __indexOfFilteredItems
266 def steplist(self, **kwargs):
267 "Nombre de pas filtrés"
268 __indexOfFilteredItems = self.__filteredIndexes(**kwargs)
269 return list(__indexOfFilteredItems)
271 # ---------------------------------------------------------
274 Renvoie la série, contenant à chaque pas, la valeur moyenne des données
275 au pas. Il faut que le type de base soit compatible avec les types
279 return [numpy.matrix(item).mean() for item in self.__values]
281 raise TypeError("Base type is incompatible with numpy")
283 def stds(self, ddof=0):
285 Renvoie la série, contenant à chaque pas, l'écart-type des données
286 au pas. Il faut que le type de base soit compatible avec les types
289 ddof : c'est le nombre de degrés de liberté pour le calcul de
290 l'écart-type, qui est dans le diviseur. Inutile avant Numpy 1.1
293 if numpy.version.version >= '1.1.0':
294 return [numpy.matrix(item).std(ddof=ddof) for item in self.__values]
296 return [numpy.matrix(item).std() for item in self.__values]
298 raise TypeError("Base type is incompatible with numpy")
302 Renvoie la série, contenant à chaque pas, la somme des données au pas.
303 Il faut que le type de base soit compatible avec les types élémentaires
307 return [numpy.matrix(item).sum() for item in self.__values]
309 raise TypeError("Base type is incompatible with numpy")
313 Renvoie la série, contenant à chaque pas, le minimum des données au pas.
314 Il faut que le type de base soit compatible avec les types élémentaires
318 return [numpy.matrix(item).min() for item in self.__values]
320 raise TypeError("Base type is incompatible with numpy")
324 Renvoie la série, contenant à chaque pas, la maximum des données au pas.
325 Il faut que le type de base soit compatible avec les types élémentaires
329 return [numpy.matrix(item).max() for item in self.__values]
331 raise TypeError("Base type is incompatible with numpy")
338 geometry = "600x400",
342 "Préparation des plots"
344 # Vérification de la disponibilité du module 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 Gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -persist -geometry '+geometry
352 Gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -geometry '+geometry
355 self.__g = Gnuplot.Gnuplot() # persist=1
356 self.__g('set terminal '+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
415 if not self.__dynamic:
416 self.__preplots(title, xlabel, ylabel, ltitle, geometry, persist, pause )
418 self.__dynamic = True
419 if len(self.__values) == 0: return 0
421 # Tracé du ou des vecteurs demandés
423 if step is not None and step < len(self.__values):
425 elif item is not None and item < len(self.__values):
428 indexes = indexes + list(range(len(self.__values)))
431 for index in indexes:
432 self.__g('set title "'+str(title)+' (pas '+str(index)+')"')
433 if isinstance(steps,list) or isinstance(steps,numpy.ndarray):
436 Steps = list(range(len(self.__values[index])))
438 self.__g.plot( Gnuplot.Data( Steps, self.__values[index], title=ltitle ) )
442 stepfilename = "%s_%03i.ps"%(filename,i)
443 if os.path.isfile(stepfilename):
444 raise ValueError("Error: a file with this name \"%s\" already exists."%stepfilename)
445 self.__g.hardcopy(filename=stepfilename, color=1)
447 eval(input('Please press return to continue...\n'))
451 Affichage dans le cas du suivi dynamique de la variable
453 if self.__dynamic and len(self.__values) < 2: return 0
455 self.__g('set title "'+str(self.__title))
456 Steps = list(range(len(self.__values)))
457 self.__g.plot( Gnuplot.Data( Steps, self.__values, title=self.__ltitle ) )
460 eval(input('Please press return to continue...\n'))
462 # ---------------------------------------------------------
465 Renvoie la moyenne sur toutes les valeurs sans tenir compte de la
466 longueur des pas. Il faut que le type de base soit compatible avec
467 les types élémentaires numpy.
470 if self.__basetype in [int, float]:
471 return float( numpy.array(self.__values).mean() )
473 return numpy.array(self.__values).mean(axis=0)
475 raise TypeError("Base type is incompatible with numpy")
477 def std(self, ddof=0):
479 Renvoie l'écart-type de toutes les valeurs sans tenir compte de la
480 longueur des pas. Il faut que le type de base soit compatible avec
481 les types élémentaires numpy.
483 ddof : c'est le nombre de degrés de liberté pour le calcul de
484 l'écart-type, qui est dans le diviseur. Inutile avant Numpy 1.1
487 if numpy.version.version >= '1.1.0':
488 return numpy.array(self.__values).std(ddof=ddof,axis=0)
490 return numpy.array(self.__values).std(axis=0)
492 raise TypeError("Base type is incompatible with numpy")
496 Renvoie la somme de toutes les valeurs sans tenir compte de la
497 longueur des pas. Il faut que le type de base soit compatible avec
498 les types élémentaires numpy.
501 return numpy.array(self.__values).sum(axis=0)
503 raise TypeError("Base type is incompatible with numpy")
507 Renvoie le minimum de toutes les valeurs sans tenir compte de la
508 longueur des pas. Il faut que le type de base soit compatible avec
509 les types élémentaires numpy.
512 return numpy.array(self.__values).min(axis=0)
514 raise TypeError("Base type is incompatible with numpy")
518 Renvoie le maximum de toutes les valeurs sans tenir compte de la
519 longueur des pas. Il faut que le type de base soit compatible avec
520 les types élémentaires numpy.
523 return numpy.array(self.__values).max(axis=0)
525 raise TypeError("Base type is incompatible with numpy")
529 Renvoie la somme cumulée de toutes les valeurs sans tenir compte de la
530 longueur des pas. Il faut que le type de base soit compatible avec
531 les types élémentaires numpy.
534 return numpy.array(self.__values).cumsum(axis=0)
536 raise TypeError("Base type is incompatible with numpy")
538 # On pourrait aussi utiliser les autres attributs d'une "matrix", comme
547 geometry = "600x400",
553 Renvoie un affichage unique pour l'ensemble des valeurs à chaque pas, si
554 elles sont compatibles avec un affichage Gnuplot (donc essentiellement
555 un vecteur). Si l'argument "step" existe dans la liste des pas de
556 stockage effectués, renvoie l'affichage de la valeur stockée à ce pas
557 "step". Si l'argument "item" est correct, renvoie l'affichage de la
558 valeur stockée au numéro "item".
561 - steps : liste unique des pas de l'axe des X, ou None si c'est
562 la numérotation par défaut
563 - title : base du titre général, qui sera automatiquement
564 complétée par la mention du pas
565 - xlabel : label de l'axe des X
566 - ylabel : label de l'axe des Y
567 - ltitle : titre associé au vecteur tracé
568 - geometry : taille en pixels de la fenêtre et position du coin haut
569 gauche, au format X11 : LxH+X+Y (défaut : 600x400)
570 - filename : nom de fichier Postscript pour une sauvegarde
571 - persist : booléen indiquant que la fenêtre affichée sera
572 conservée lors du passage au dessin suivant
573 Par défaut, persist = False
574 - pause : booléen indiquant une pause après chaque tracé, et
576 Par défaut, pause = True
579 # Vérification de la disponibilité du module Gnuplot
581 raise ImportError("The Gnuplot module is required to plot the object.")
583 # Vérification et compléments sur les paramètres d'entrée
585 Gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -persist -geometry '+geometry
587 Gnuplot.GnuplotOpts.gnuplot_command = 'gnuplot -geometry '+geometry
590 if isinstance(steps,list) or isinstance(steps, numpy.ndarray):
593 Steps = list(range(len(self.__values[0])))
594 self.__g = Gnuplot.Gnuplot() # persist=1
595 self.__g('set terminal '+Gnuplot.GnuplotOpts.default_term)
596 self.__g('set style data lines')
598 self.__g('set autoscale')
599 self.__g('set title "'+str(title) +'"')
600 self.__g('set xlabel "'+str(xlabel)+'"')
601 self.__g('set ylabel "'+str(ylabel)+'"')
603 # Tracé du ou des vecteurs demandés
604 indexes = list(range(len(self.__values)))
605 self.__g.plot( Gnuplot.Data( Steps, self.__values[indexes.pop(0)], title=ltitle+" (pas 0)" ) )
606 for index in indexes:
607 self.__g.replot( Gnuplot.Data( Steps, self.__values[index], title=ltitle+" (pas %i)"%index ) )
610 self.__g.hardcopy(filename=filename, color=1)
612 eval(input('Please press return to continue...\n'))
614 # ---------------------------------------------------------
615 def setDataObserver(self, HookFunction = None, HookParameters = None, Scheduler = None):
617 Association à la variable d'un triplet définissant un observer
619 Le Scheduler attendu est une fréquence, une simple liste d'index ou un
623 # Vérification du Scheduler
624 # -------------------------
626 if isinstance(Scheduler,int): # Considéré comme une fréquence à partir de 0
627 Schedulers = range( 0, maxiter, int(Scheduler) )
628 elif isinstance(Scheduler,range): # Considéré comme un itérateur
629 Schedulers = Scheduler
630 elif isinstance(Scheduler,(list,tuple)): # Considéré comme des index explicites
631 Schedulers = [iLong(i) for i in Scheduler] # map( long, Scheduler )
632 else: # Dans tous les autres cas, activé par défaut
633 Schedulers = range( 0, maxiter )
635 # Stockage interne de l'observer dans la variable
636 # -----------------------------------------------
637 self.__dataobservers.append( [HookFunction, HookParameters, Schedulers] )
639 def removeDataObserver(self, HookFunction = None, AllObservers = False):
641 Suppression d'un observer nommé sur la variable.
643 On peut donner dans HookFunction la meme fonction que lors de la
644 définition, ou un simple string qui est le nom de la fonction. Si
645 AllObservers est vrai, supprime tous les observers enregistrés.
647 if hasattr(HookFunction,"func_name"):
648 name = str( HookFunction.func_name )
649 elif hasattr(HookFunction,"__name__"):
650 name = str( HookFunction.__name__ )
651 elif isinstance(HookFunction,str):
652 name = str( HookFunction )
658 for [hf, hp, hs] in self.__dataobservers:
660 if name is hf.__name__ or AllObservers: index_to_remove.append( i )
661 index_to_remove.reverse()
662 for i in index_to_remove:
663 self.__dataobservers.pop( i )
664 return len(index_to_remove)
666 def hasDataObserver(self):
667 return bool(len(self.__dataobservers) > 0)
669 # ==============================================================================
670 class SchedulerTrigger(object):
672 Classe générale d'interface de type Scheduler/Trigger
675 simplifiedCombo = None,
677 endTime = int( 1e9 ),
684 # ==============================================================================
685 class OneScalar(Persistence):
687 Classe définissant le stockage d'une valeur unique réelle (float) par pas.
689 Le type de base peut être changé par la méthode "basetype", mais il faut que
690 le nouveau type de base soit compatible avec les types par éléments de
691 numpy. On peut même utiliser cette classe pour stocker des vecteurs/listes
692 ou des matrices comme dans les classes suivantes, mais c'est déconseillé
693 pour conserver une signification claire des noms.
695 def __init__(self, name="", unit="", basetype = float):
696 Persistence.__init__(self, name, unit, basetype)
698 class OneIndex(Persistence):
700 Classe définissant le stockage d'une valeur unique entière (int) par pas.
702 def __init__(self, name="", unit="", basetype = int):
703 Persistence.__init__(self, name, unit, basetype)
705 class OneVector(Persistence):
707 Classe de stockage d'une liste de valeurs numériques homogènes par pas. Ne
708 pas utiliser cette classe pour des données hétérogènes, mais "OneList".
710 def __init__(self, name="", unit="", basetype = numpy.ravel):
711 Persistence.__init__(self, name, unit, basetype)
713 class OneMatrix(Persistence):
715 Classe de stockage d'une matrice de valeurs (numpy.matrix) par pas.
717 def __init__(self, name="", unit="", basetype = numpy.matrix):
718 Persistence.__init__(self, name, unit, basetype)
720 class OneList(Persistence):
722 Classe de stockage d'une liste de valeurs hétérogènes (list) par pas. Ne pas
723 utiliser cette classe pour des données numériques homogènes, mais
726 def __init__(self, name="", unit="", basetype = list):
727 Persistence.__init__(self, name, unit, basetype)
730 "Fonction transparente, sans effet sur son argument"
733 class OneNoType(Persistence):
735 Classe de stockage d'un objet sans modification (cast) de type. Attention,
736 selon le véritable type de l'objet stocké à chaque pas, les opérations
737 arithmétiques à base de numpy peuvent être invalides ou donner des résultats
738 inattendus. Cette classe n'est donc à utiliser qu'à bon escient
739 volontairement, et pas du tout par défaut.
741 def __init__(self, name="", unit="", basetype = NoType):
742 Persistence.__init__(self, name, unit, basetype)
744 # ==============================================================================
745 class CompositePersistence(object):
747 Structure de stockage permettant de rassembler plusieurs objets de
750 Des objets par défaut sont prévus, et des objets supplémentaires peuvent
753 def __init__(self, name="", defaults=True):
757 La gestion interne des données est exclusivement basée sur les variables
758 initialisées ici (qui ne sont pas accessibles depuis l'extérieur des
759 objets comme des attributs) :
760 __StoredObjects : objets de type persistence collectés dans cet objet
762 self.__name = str(name)
764 self.__StoredObjects = {}
766 # Definition des objets par defaut
767 # --------------------------------
769 self.__StoredObjects["Informations"] = OneNoType("Informations")
770 self.__StoredObjects["Background"] = OneVector("Background", basetype=numpy.array)
771 self.__StoredObjects["BackgroundError"] = OneMatrix("BackgroundError")
772 self.__StoredObjects["Observation"] = OneVector("Observation", basetype=numpy.array)
773 self.__StoredObjects["ObservationError"] = OneMatrix("ObservationError")
774 self.__StoredObjects["Analysis"] = OneVector("Analysis", basetype=numpy.array)
775 self.__StoredObjects["AnalysisError"] = OneMatrix("AnalysisError")
776 self.__StoredObjects["Innovation"] = OneVector("Innovation", basetype=numpy.array)
777 self.__StoredObjects["KalmanGainK"] = OneMatrix("KalmanGainK")
778 self.__StoredObjects["OperatorH"] = OneMatrix("OperatorH")
779 self.__StoredObjects["RmsOMA"] = OneScalar("RmsOMA")
780 self.__StoredObjects["RmsOMB"] = OneScalar("RmsOMB")
781 self.__StoredObjects["RmsBMA"] = OneScalar("RmsBMA")
784 def store(self, name=None, value=None, **kwargs):
786 Stockage d'une valeur "value" pour le "step" dans la variable "name".
788 if name is None: raise ValueError("Storable object name is required for storage.")
789 if name not in self.__StoredObjects.keys():
790 raise ValueError("No such name '%s' exists in storable objects."%name)
791 self.__StoredObjects[name].store( value=value, **kwargs )
793 def add_object(self, name=None, persistenceType=Persistence, basetype=None ):
795 Ajoute dans les objets stockables un nouvel objet défini par son nom, son
796 type de Persistence et son type de base à chaque pas.
798 if name is None: raise ValueError("Object name is required for adding an object.")
799 if name in self.__StoredObjects.keys():
800 raise ValueError("An object with the same name '%s' already exists in storable objects. Choose another one."%name)
802 self.__StoredObjects[name] = persistenceType( name=str(name) )
804 self.__StoredObjects[name] = persistenceType( name=str(name), basetype=basetype )
806 def get_object(self, name=None ):
808 Renvoie l'objet de type Persistence qui porte le nom demandé.
810 if name is None: raise ValueError("Object name is required for retrieving an object.")
811 if name not in self.__StoredObjects.keys():
812 raise ValueError("No such name '%s' exists in stored objects."%name)
813 return self.__StoredObjects[name]
815 def set_object(self, name=None, objet=None ):
817 Affecte directement un 'objet' qui porte le nom 'name' demandé.
818 Attention, il n'est pas effectué de vérification sur le type, qui doit
819 comporter les méthodes habituelles de Persistence pour que cela
822 if name is None: raise ValueError("Object name is required for setting an object.")
823 if name in self.__StoredObjects.keys():
824 raise ValueError("An object with the same name '%s' already exists in storable objects. Choose another one."%name)
825 self.__StoredObjects[name] = objet
827 def del_object(self, name=None ):
829 Supprime un objet de la liste des objets stockables.
831 if name is None: raise ValueError("Object name is required for retrieving an object.")
832 if name not in self.__StoredObjects.keys():
833 raise ValueError("No such name '%s' exists in stored objects."%name)
834 del self.__StoredObjects[name]
836 # ---------------------------------------------------------
837 # Méthodes d'accès de type dictionnaire
838 def __getitem__(self, name=None ):
839 "x.__getitem__(y) <==> x[y]"
840 return self.get_object( name )
842 def __setitem__(self, name=None, objet=None ):
843 "x.__setitem__(i, y) <==> x[i]=y"
844 self.set_object( name, objet )
847 "D.keys() -> list of D's keys"
848 return self.get_stored_objects(hideVoidObjects = False)
851 "D.values() -> list of D's values"
852 return self.__StoredObjects.values()
855 "D.items() -> list of D's (key, value) pairs, as 2-tuples"
856 return self.__StoredObjects.items()
858 # ---------------------------------------------------------
859 def get_stored_objects(self, hideVoidObjects = False):
860 "Renvoie la liste des objets présents"
861 objs = self.__StoredObjects.keys()
866 if len(self.__StoredObjects[k]) > 0: usedObjs.append( k )
873 # ---------------------------------------------------------
874 def save_composite(self, filename=None, mode="pickle", compress="gzip"):
876 Enregistre l'objet dans le fichier indiqué selon le "mode" demandé,
877 et renvoi le nom du fichier
880 if compress == "gzip":
881 filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl.gz"
882 elif compress == "bzip2":
883 filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl.bz2"
885 filename = os.tempnam( os.getcwd(), 'dacp' ) + ".pkl"
887 filename = os.path.abspath( filename )
890 if compress == "gzip":
891 output = gzip.open( filename, 'wb')
892 elif compress == "bzip2":
893 output = bz2.BZ2File( filename, 'wb')
895 output = open( filename, 'wb')
896 pickle.dump(self, output)
899 raise ValueError("Save mode '%s' unknown. Choose another one."%mode)
903 def load_composite(self, filename=None, mode="pickle", compress="gzip"):
905 Recharge un objet composite sauvé en fichier
908 raise ValueError("A file name if requested to load a composite.")
910 filename = os.path.abspath( filename )
913 if compress == "gzip":
914 pkl_file = gzip.open( filename, 'rb')
915 elif compress == "bzip2":
916 pkl_file = bz2.BZ2File( filename, 'rb')
918 pkl_file = open(filename, 'rb')
919 output = pickle.load(pkl_file)
920 for k in output.keys():
923 raise ValueError("Load mode '%s' unknown. Choose another one."%mode)
927 # ==============================================================================
928 if __name__ == "__main__":
929 print('\n AUTODIAGNOSTIC \n')