Salome HOME
CCAR: Mise a niveau Noyau avec Aster 7.2.11 + correction bug sur les blocs
[tools/eficas.git] / Noyau / N_MACRO_ETAPE.py
1 #@ MODIF N_MACRO_ETAPE Noyau  DATE 26/09/2003   AUTEUR DURAND C.DURAND 
2 #            CONFIGURATION MANAGEMENT OF EDF VERSION
3 # ======================================================================
4 # COPYRIGHT (C) 1991 - 2002  EDF R&D                  WWW.CODE-ASTER.ORG
5 # THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
6 # IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
7 # THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR   
8 # (AT YOUR OPTION) ANY LATER VERSION.                                 
9 #
10 # THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT 
11 # WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF          
12 # MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU    
13 # GENERAL PUBLIC LICENSE FOR MORE DETAILS.                            
14 #
15 # YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE   
16 # ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,       
17 #    1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.      
18 #                                                                       
19 #                                                                       
20 # ======================================================================
21 """ 
22     Ce module contient la classe MACRO_ETAPE qui sert à vérifier et à exécuter
23     une commande
24 """
25
26 # Modules Python
27 import types,sys,string
28 import traceback
29
30 # Modules EFICAS
31 import N_MCCOMPO
32 import N_ETAPE
33 from N_Exception import AsException
34 import N_utils
35 from N_utils import AsType
36
37 class MACRO_ETAPE(N_ETAPE.ETAPE):
38    """
39
40    """
41    nature = "COMMANDE"
42    def __init__(self,oper=None,reuse=None,args={}):
43       """
44          Attributs :
45
46           - definition : objet portant les attributs de définition d'une étape 
47                          de type macro-commande. Il est initialisé par 
48                           l'argument oper.
49
50           - reuse : indique le concept d'entrée réutilisé. Il se trouvera donc
51                     en sortie si les conditions d'exécution de l'opérateur 
52                     l'autorise
53
54           - valeur : arguments d'entrée de type mot-clé=valeur. Initialisé 
55                      avec l'argument args.
56
57       """
58       self.definition=oper
59       self.reuse=reuse
60       self.valeur=args
61       self.nettoiargs()
62       self.parent=CONTEXT.get_current_step()
63       self.etape = self
64       self.nom=oper.nom
65       self.idracine=oper.label
66       self.appel=N_utils.callee_where()
67       self.mc_globaux={}
68       self.g_context={}
69       # Contexte courant
70       self.current_context={}
71       self.index_etape_courante=0
72       self.etapes=[]
73       self.sds=[]
74       #  Dans le cas d'une macro écrite en Python, l'attribut Outputs est un 
75       #  dictionnaire qui contient les concepts produits de sortie 
76       #  (nom : ASSD) déclarés dans la fonction sd_prod
77       self.Outputs={}
78       self.sd=None
79       self.actif=1
80       self.sdprods=[]
81       self.make_register()
82
83    def make_register(self):
84       """
85          Initialise les attributs jdc, id, niveau et réalise les enregistrements
86          nécessaires
87       """
88       if self.parent :
89          self.jdc = self.parent.get_jdc_root()
90          self.id=self.parent.register(self)
91          self.niveau=None
92       else:
93          self.jdc = self.parent =None
94          self.id=None
95          self.niveau=None
96
97    def Build_sd(self,nom):
98       """
99          Construit le concept produit de l'opérateur. Deux cas 
100          peuvent se présenter :
101         
102          - le parent n'est pas défini. Dans ce cas, l'étape prend en charge 
103            la création et le nommage du concept.
104
105          - le parent est défini. Dans ce cas, l'étape demande au parent la 
106            création et le nommage du concept.
107
108       """
109       if not self.isactif():return
110       self.sdnom=nom
111       try:
112          # On positionne la macro self en tant que current_step pour que les 
113          # étapes créées lors de l'appel à sd_prod et à op_init aient la macro
114          #  comme parent 
115          self.set_current_step()
116          if self.parent:
117             sd= self.parent.create_sdprod(self,nom)
118             if type(self.definition.op_init) == types.FunctionType: 
119                apply(self.definition.op_init,(self,self.parent.g_context))
120          else:
121             sd=self.get_sd_prod()
122             if sd != None and self.reuse == None:
123                # On ne nomme le concept que dans le cas de non reutilisation 
124                # d un concept
125                sd.nom=nom
126          self.reset_current_step()
127       except AsException,e:
128          self.reset_current_step()
129          raise AsException("Etape ",self.nom,'ligne : ',self.appel[0],
130                               'fichier : ',self.appel[1],e)
131       except (EOFError,self.jdc.UserError):
132          # Le retablissement du step courant n'est pas strictement necessaire. On le fait pour des raisons de coherence
133          self.reset_current_step()
134          raise
135       except :
136          self.reset_current_step()
137          l=traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2])
138          raise AsException("Etape ",self.nom,'ligne : ',self.appel[0],
139                            'fichier : ',self.appel[1]+'\n',
140                             string.join(l))
141
142       self.Execute()
143       return sd
144
145    def get_sd_prod(self):
146       """
147         Retourne le concept résultat d'une macro étape
148         La difference avec une etape ou une proc-etape tient a ce que
149          le concept produit peut exister ou pas
150         Si sd_prod == None le concept produit n existe pas on retourne None
151         Deux cas :
152          cas 1 : sd_prod  n'est pas une fonction
153                  il s'agit d'une sous classe de ASSD
154                  on construit le sd à partir de cette classe
155                  et on le retourne
156          cas 2 : sd_prod est une fonction
157                   on l'évalue avec les mots-clés de l'étape (mc_liste)
158                  on construit le sd à partir de la classe obtenue
159                  et on le retourne
160       """
161       sd_prod=self.definition.sd_prod
162       self.typret=None
163       if type(self.definition.sd_prod) == types.FunctionType:
164         d=self.cree_dict_valeurs(self.mc_liste)
165         try:
166           # la sd_prod d'une macro a l'objet macro_etape lui meme en premier argument
167           # Comme sd_prod peut invoquer la méthode type_sdprod qui ajoute
168           # les concepts produits dans self.sdprods, il faut le mettre à zéro avant de l'appeler
169           self.sdprods=[]
170           sd_prod= apply(sd_prod,(self,),d)
171         except (EOFError,self.jdc.UserError):
172           raise
173         except:
174           if CONTEXT.debug: traceback.print_exc()
175           l=traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2])
176           raise AsException("impossible d affecter un type au resultat\n",string.join(l[2:]))
177
178       # on teste maintenant si la SD est réutilisée ou s'il faut la créer
179       if self.definition.reentrant != 'n' and self.reuse:
180         # Le concept produit est specifie reutilise (reuse=xxx). C'est une erreur mais non fatale.
181         # Elle sera traitee ulterieurement.
182         self.sd=self.reuse
183       else:
184         if sd_prod == None:
185           self.sd=None
186         else:
187           self.sd= sd_prod(etape=self)
188           self.typret=sd_prod
189           # Si la commande est obligatoirement reentrante et reuse n'a pas ete specifie, c'est une erreur. 
190           # On ne fait rien ici. L'erreur sera traitee par la suite. 
191       return self.sd
192
193    def get_type_produit(self,force=0):
194       """
195            Retourne le type du concept résultat de l'étape et eventuellement type
196             les concepts produits "à droite" du signe égal (en entrée)
197            Deux cas :
198             cas 1 : sd_prod de oper n'est pas une fonction
199                     il s'agit d'une sous classe de ASSD
200                     on retourne le nom de la classe
201             cas 2 : il s'agit d'une fonction
202                     on l'évalue avec les mots-clés de l'étape (mc_liste)
203                     et on retourne son résultat
204       """
205       if not force and hasattr(self,'typret'): return self.typret
206       if type(self.definition.sd_prod) == types.FunctionType:
207         d=self.cree_dict_valeurs(self.mc_liste)
208         try:
209           # Comme sd_prod peut invoquer la méthode type_sdprod qui ajoute
210           # les concepts produits dans self.sdprods, il faut le mettre à zéro
211           self.sdprods=[]
212           sd_prod= apply(self.definition.sd_prod,(self,),d)
213         except:
214           #traceback.print_exc()
215           return None
216       else:
217         sd_prod=self.definition.sd_prod
218       return sd_prod
219
220    def get_contexte_avant(self,etape):
221       """
222           Retourne le dictionnaire des concepts connus avant etape
223           pour les commandes internes a la macro
224           On tient compte des commandes qui modifient le contexte
225           comme DETRUIRE ou les macros
226       """
227       # L'étape courante pour laquelle le contexte a été calculé est 
228       # mémorisée dans self.index_etape_courante
229       # Si on insère des commandes (par ex, dans EFICAS), il faut
230       # préalablement remettre ce pointeur à 0
231       if etape:
232          index_etape=self.etapes.index(etape)
233       else:
234          index_etape=len(self.etapes)
235
236       if index_etape >= self.index_etape_courante:
237          # On calcule le contexte en partant du contexte existant
238          d=self.current_context
239          liste_etapes=self.etapes[self.index_etape_courante:index_etape]
240       else:
241          d=self.current_context={}
242          liste_etapes=self.etapes
243
244       for e in liste_etapes:
245         if e is etape:
246            break
247         if e.isactif():
248            e.update_context(d)
249       self.index_etape_courante=index_etape
250       return d
251
252    def supprime(self):
253       """
254          Méthode qui supprime toutes les références arrières afin que 
255          l'objet puisse etre correctement détruit par le garbage collector
256       """
257       N_MCCOMPO.MCCOMPO.supprime(self)
258       self.jdc=None
259       self.appel=None
260       if self.sd : self.sd.supprime()
261       for concept in self.sdprods:
262          concept.supprime()
263       for etape in self.etapes:
264          etape.supprime()
265
266    def type_sdprod(self,co,t):
267       """
268            Cette methode a pour fonction de typer le concept co avec le type t
269             dans les conditions suivantes
270             1- co est un concept produit de self
271             2- co est un concept libre : on le type et on l attribue à self
272            Elle enregistre egalement les concepts produits (on fait l hypothese
273             que la liste sdprods a été correctement initialisee, vide probablement)
274       """
275       if not hasattr(co,'etape'):
276          # Le concept vaut None probablement. On ignore l'appel
277          return
278
279       if co.etape == None:
280          # le concept est libre
281          co.etape=self
282          co.__class__ = t
283          self.sdprods.append(co)
284       elif co.etape== self:
285          # le concept est produit par self
286          co.__class__ = t
287          self.sdprods.append(co)
288       elif co.etape== self.parent:
289          # le concept est produit par la macro superieure
290          # on transfere la propriete
291          # On verifie que le type du concept existant co.__class__ est un sur type de celui attendu
292          # Cette règle est normalement cohérente avec les règles de vérification des mots-clés
293          if not issubclass(t,co.__class__):
294             raise AsException("Le type du concept produit %s devrait etre une sur classe de %s" %(co.__class__,t))
295          co.etape=self
296          co.__class__ = t
297          self.sdprods.append(co)
298       elif self.issubstep(co.etape):
299          # Le concept est propriété d'une sous etape de self. Il doit etre considere
300          # comme produit par la macro => ajout dans self.sdprods
301          self.sdprods.append(co)
302       else:
303          # le concept est produit par une autre étape
304          return
305
306    def issubstep(self,etape):
307       """ 
308           Cette methode retourne un entier indiquant si etape est une
309           sous etape de la macro self ou non
310           1 = oui
311           0 = non
312       """
313       if etape in self.etapes:return 1
314       for etap in self.etapes:
315         if etap.issubstep(etape):return 1
316       return 0
317
318    def register(self,etape):
319       """ 
320           Enregistrement de etape dans le contexte de la macro : liste etapes 
321           et demande d enregistrement global aupres du JDC
322       """
323       self.etapes.append(etape)
324       idetape=self.jdc.g_register(etape)
325       return idetape
326
327    def reg_sd(self,sd):
328       """ 
329            Methode appelee dans l __init__ d un ASSD a sa creation pour
330            s enregistrer (reserve aux ASSD créés au sein d'une MACRO)
331       """
332       self.sds.append(sd)
333       return self.jdc.o_register(sd)
334
335    def create_sdprod(self,etape,nomsd):
336       """ 
337           Intention : Cette methode doit fabriquer le concept produit retourne
338                   par l'etape etape et le nommer.
339                   Elle est appelée à l'initiative de l'etape
340                   pendant le processus de construction de cette etape : methode __call__
341                   de la classe CMD (OPER ou MACRO)
342                   Ce travail est réalisé par le contexte supérieur (etape.parent)
343                   car dans certains cas, le concept ne doit pas etre fabriqué mais
344                   l'etape doit simplement utiliser un concept préexistant.
345                   Cas 1 : etape.reuse != None : le concept est réutilisé
346                   Cas 2 : l'étape appartient à une macro qui a déclaré un concept
347                           de sortie qui doit etre produit par cette etape.
348       """
349       if self.Outputs.has_key(nomsd):
350          # Il s'agit d'un concept de sortie de la macro. Il ne faut pas le créer
351          # Il faut quand meme appeler la fonction sd_prod si elle existe.
352          # get_type_produit le fait et donne le type attendu par la commande pour verification ultérieure.
353          sdprod=etape.get_type_produit()
354          sd=self.Outputs[nomsd]
355          # On verifie que le type du concept existant sd.__class__ est un sur type de celui attendu
356          # Cette règle est normalement cohérente avec les règles de vérification des mots-clés
357          if not issubclass(sdprod,sd.__class__):
358             raise AsException("Le type du concept produit %s devrait etre une sur classe de %s" %(sd.__class__,sdprod))
359          # La propriete du concept est transferee a l'etape avec le type attendu par l'étape
360          etape.sd=sd
361          sd.etape=etape
362          # On donne au concept le type produit par la sous commande.
363          # Le principe est le suivant : apres avoir verifie que le type deduit par la sous commande
364          # est bien coherent avec celui initialement affecte par la macro (voir ci dessus)
365          # on affecte au concept ce type car il peut etre plus precis (derive, en general)
366          sd.__class__=sdprod
367          # On force également le nom stocké dans l'attribut sdnom : on lui donne le nom 
368          # du concept associé à nomsd
369          etape.sdnom=sd.nom
370       elif etape.definition.reentrant != 'n' and etape.reuse != None:
371          # On est dans le cas d'une commande avec reutilisation d'un concept existant
372          # get_sd_prod fait le necessaire : verifications, associations, etc. mais ne cree 
373          # pas un nouveau concept. Il retourne le concept reutilise
374          sd= etape.get_sd_prod()
375          # Dans le cas d'un concept nomme automatiquement : _xxx, __xxx,
376          # On force le nom stocke dans l'attribut sdnom  de l'objet etape : on lui donne le nom 
377          # du concept  reutilise (sd ou etape.reuse c'est pareil)
378          # Ceci est indispensable pour eviter des erreurs lors des verifications des macros
379          # En effet une commande avec reutilisation d'un concept verifie que le nom de 
380          # la variable a gauche du signe = est le meme que celui du concept reutilise.
381          # Lorsqu'une telle commande apparait dans une macro, on supprime cette verification.
382          if (etape.sdnom == '' or etape.sdnom[0] == '_'):
383             etape.sdnom=sd.nom
384       else:
385          # On est dans le cas de la creation d'un nouveau concept
386          sd= etape.get_sd_prod()
387          if sd != None :
388             self.NommerSdprod(sd,nomsd)
389       return sd
390
391    def NommerSdprod(self,sd,sdnom,restrict='non'):
392       """ 
393           Cette methode est appelee par les etapes internes de la macro
394           La macro appelle le JDC pour valider le nommage
395           On considere que l espace de nom est unique et géré par le JDC
396           Si le nom est deja utilise, l appel leve une exception
397           Si restrict=='non', on insere le concept dans le contexte de la macro
398           Si restrict=='oui', on n'insere pas le concept dans le contexte de la macro
399       """
400       # Normalement, lorsqu'on appelle cette methode, on ne veut nommer que des concepts nouvellement crees.
401       # Le filtrage sur les concepts a creer ou a ne pas creer est fait dans la methode
402       # create_sdprod. La seule chose a verifier apres conversion eventuelle du nom
403       # est de verifier que le nom n'est pas deja attribue. Ceci est fait en delegant
404       # au JDC par l'intermediaire du parent.
405
406       #XXX attention inconsistence : prefix et gcncon ne sont pas 
407       # définis dans le package Noyau. La methode NommerSdprod pour
408       # les macros devrait peut etre etre déplacée dans Build ???
409
410       if CONTEXT.debug : print "MACRO.NommerSdprod: ",sd,sdnom
411
412       if hasattr(self,'prefix'):
413         # Dans le cas de l'include_materiau on ajoute un prefixe au nom du concept
414         if sdnom != self.prefix:sdnom=self.prefix+sdnom
415
416       if self.Outputs.has_key(sdnom):
417         # Il s'agit d'un concept de sortie de la macro produit par une sous commande
418         sdnom=self.Outputs[sdnom].nom
419       elif sdnom != '' and sdnom[0] == '_':
420         # Si le nom du concept commence par le caractere _ on lui attribue
421         # un identificateur JEVEUX construit par gcncon et respectant
422         # la regle gcncon legerement adaptee ici
423         # nom commencant par __ : il s'agit de concepts qui seront detruits
424         # nom commencant par _ : il s'agit de concepts intermediaires qui seront gardes
425         # ATTENTION : il faut traiter différemment les concepts dont le nom
426         # commence par _ mais qui sont des concepts nommés automatiquement par
427         # une éventuelle sous macro.
428         # Le test suivant n'est pas tres rigoureux mais permet de fonctionner pour le moment (a améliorer)
429         if sdnom[1] in string.digits:
430           # Ce concept provient probablement d'une macro appelee par self
431           pass
432         elif sdnom[1] == '_':
433           sdnom=self.gcncon('.')
434         else:
435           sdnom=self.gcncon('_')
436       else:
437         # On est dans le cas d'un nom de concept global. 
438         pass
439
440       if restrict == 'non':
441          # On demande le nommage au parent mais sans ajout du concept dans le contexte du parent
442          # car on va l'ajouter dans le contexte de la macro
443          self.parent.NommerSdprod(sd,sdnom,restrict='oui')
444          # On ajoute dans le contexte de la macro les concepts nommes
445          # Ceci est indispensable pour les CO (macro) dans un INCLUDE
446          self.g_context[sdnom]=sd
447       else:
448          # La demande de nommage vient probablement d'une macro qui a mis
449          # le concept dans son contexte. On ne traite plus que le nommage (restrict="oui")
450          self.parent.NommerSdprod(sd,sdnom,restrict='oui')
451
452    def delete_concept_after_etape(self,etape,sd):
453       """
454           Met à jour les étapes de la MACRO  qui sont après etape suite à
455           la disparition du concept sd
456       """
457       # Cette methode est définie dans le noyau mais ne sert que pendant la phase de creation
458       # des etapes et des concepts. Il n'y a aucun traitement particulier à réaliser
459       # Dans d'autres conditions, il faudrait surcharger cette méthode.
460       return
461
462    def accept(self,visitor):
463       """
464          Cette methode permet de parcourir l'arborescence des objets
465          en utilisant le pattern VISITEUR
466       """
467       visitor.visitMACRO_ETAPE(self)
468
469    def update_context(self,d):
470       """
471          Met à jour le contexte contenu dans le dictionnaire d
472          Une MACRO_ETAPE peut ajouter plusieurs concepts dans le contexte
473          Une fonction enregistree dans op_init peut egalement modifier le contexte
474       """
475       if type(self.definition.op_init) == types.FunctionType:
476         apply(self.definition.op_init,(self,d))
477       if self.sd != None:d[self.sd.nom]=self.sd
478       for co in self.sdprods:
479         d[co.nom]=co
480
481    def make_include(self,unite=None):
482       """
483           Inclut un fichier dont l'unite logique est unite
484       """
485       if not unite : return
486       f,text=self.get_file(unite=unite,fic_origine=self.parent.nom)
487       self.fichier_init = f
488       if f == None:return
489       self.make_contexte(f,text)
490
491    def make_poursuite(self):
492       """
493           Inclut un fichier poursuite
494       """
495       try:
496          f,text=self.get_file(fic_origine=self.parent.nom)
497       except:
498          raise AsException("Impossible d'ouvrir la base pour une poursuite")
499       self.fichier_init=f
500       if f == None:return
501       self.make_contexte(f,text)
502
503    def make_contexte(self,f,text):
504       """
505           Interprete le texte fourni (text) issu du fichier f
506           dans le contexte du parent.
507           Cette methode est utile pour le fonctionnement des
508           INCLUDE
509       """
510       # on execute le texte fourni dans le contexte forme par
511       # le contexte de l etape pere (global au sens Python)
512       # et le contexte de l etape (local au sens Python)
513       code=compile(text,f,'exec')
514       d={}
515       self.g_context = d
516       self.contexte_fichier_init = d
517       globs=self.parent.get_global_contexte()
518       exec code in globs,d
519
520    def get_global_contexte(self):
521       """
522           Cette methode retourne le contexte global fourni
523           par le parent(self) a une etape fille (l'appelant) pour
524           realiser des evaluations de texte Python (INCLUDE,...)
525       """
526       # Le contexte global est forme par concatenation du contexte
527       # du parent de self et de celui de l'etape elle meme (self)
528       d=self.parent.get_global_contexte()
529       d.update(self.g_context)
530       return d
531
532    def copy(self):
533       """ Méthode qui retourne une copie de self non enregistrée auprès du JDC
534           et sans sd
535           On surcharge la methode de ETAPE pour exprimer que les concepts crees
536           par la MACRO d'origine ne sont pas crees par la copie mais eventuellement
537           seulement utilises
538       """
539       etape=N_ETAPE.ETAPE.copy(self)
540       etape.sdprods=[]
541       return etape
542
543
544
545
546
547
548