1 #@ MODIF N_MACRO_ETAPE Noyau DATE 06/01/2003 AUTEUR ASSIRE A.ASSIRE
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.
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.
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.
20 # ======================================================================
22 Ce module contient la classe MACRO_ETAPE qui sert à vérifier et à exécuter
27 import types,sys,string
33 from N_Exception import AsException
35 from N_utils import AsType
37 class MACRO_ETAPE(N_ETAPE.ETAPE):
42 def __init__(self,oper=None,reuse=None,args={}):
46 - definition : objet portant les attributs de définition d'une étape
47 de type macro-commande. Il est initialisé par
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
54 - valeur : arguments d'entrée de type mot-clé=valeur. Initialisé
62 self.parent=CONTEXT.get_current_step()
65 self.idracine=oper.label
66 self.appel=N_utils.callee_where()
70 self.current_context={}
71 self.index_etape_courante=0
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
83 def make_register(self):
85 Initialise les attributs jdc, id, niveau et réalise les enregistrements
89 self.jdc = self.parent.get_jdc_root()
90 self.id=self.parent.register(self)
93 self.jdc = self.parent =None
97 def Build_sd(self,nom):
99 Construit le concept produit de l'opérateur. Deux cas
100 peuvent se présenter :
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.
105 - le parent est défini. Dans ce cas, l'étape demande au parent la
106 création et le nommage du concept.
109 if not self.isactif():return
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
115 self.set_current_step()
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))
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
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()
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',
145 def get_sd_prod(self):
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
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
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
161 sd_prod=self.definition.sd_prod
163 if type(self.definition.sd_prod) == types.FunctionType:
164 d=self.cree_dict_valeurs(self.mc_liste)
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
170 sd_prod= apply(sd_prod,(self,),d)
171 except (EOFError,self.jdc.UserError):
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:]))
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.
187 self.sd= sd_prod(etape=self)
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.
193 def get_type_produit(self,force=0):
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)
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
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)
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
212 sd_prod= apply(self.definition.sd_prod,(self,),d)
214 #traceback.print_exc()
217 sd_prod=self.definition.sd_prod
220 def get_contexte_avant(self,etape):
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
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
232 index_etape=self.etapes.index(etape)
234 index_etape=len(self.etapes)
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]
241 d=self.current_context={}
242 liste_etapes=self.etapes
244 for e in liste_etapes:
249 self.index_etape_courante=index_etape
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
257 N_MCCOMPO.MCCOMPO.supprime(self)
260 if self.sd : self.sd.supprime()
261 for concept in self.sdprods:
263 for etape in self.etapes:
266 def type_sdprod(self,co,t):
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)
275 if not hasattr(co,'etape'):
276 # Le concept vaut None probablement. On ignore l'appel
280 # le concept est libre
283 self.sdprods.append(co)
284 elif co.etape== self:
285 # le concept est produit par self
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))
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)
303 # le concept est produit par une autre étape
306 def issubstep(self,etape):
308 Cette methode retourne un entier indiquant si etape est une
309 sous etape de la macro self ou non
313 if etape in self.etapes:return 1
314 for etap in self.etapes:
315 if etap.issubstep(etape):return 1
318 def register(self,etape):
320 Enregistrement de etape dans le contexte de la macro : liste etapes
321 et demande d enregistrement global aupres du JDC
323 self.etapes.append(etape)
324 idetape=self.jdc.g_register(etape)
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)
333 return self.jdc.o_register(sd)
335 def create_sdprod(self,etape,nomsd):
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.
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
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)
367 # On force également le nom stocké dans l'attribut sdnom : on lui donne le nom
368 # du concept associé à nomsd
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] == '_'):
385 # On est dans le cas de la creation d'un nouveau concept
386 sd= etape.get_sd_prod()
388 self.NommerSdprod(sd,nomsd)
391 def NommerSdprod(self,sd,sdnom,restrict='non'):
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
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.
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 ???
410 if CONTEXT.debug : print "MACRO.NommerSdprod: ",sd,sdnom
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
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
432 elif sdnom[1] == '_':
433 sdnom=self.gcncon('.')
435 sdnom=self.gcncon('_')
437 # On est dans le cas d'un nom de concept global.
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
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')
452 def delete_concept_after_etape(self,etape,sd):
454 Met à jour les étapes de la MACRO qui sont après etape suite à
455 la disparition du concept sd
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.
462 def accept(self,visitor):
464 Cette methode permet de parcourir l'arborescence des objets
465 en utilisant le pattern VISITEUR
467 visitor.visitMACRO_ETAPE(self)
469 def update_context(self,d):
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
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:
481 def make_include(self,unite=None):
483 Inclut un fichier dont l'unite logique est unite
485 if not unite : return
486 f,text=self.get_file(unite=unite,fic_origine=self.parent.nom)
487 self.fichier_init = f
489 self.make_contexte(f,text)
491 def make_poursuite(self):
493 Inclut un fichier poursuite
496 f,text=self.get_file(fic_origine=self.parent.nom)
498 raise AsException("Impossible d'ouvrir la base pour une poursuite")
501 self.make_contexte(f,text)
503 def make_contexte(self,f,text):
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
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')
516 self.contexte_fichier_init = d
517 globs=self.parent.get_global_contexte()
520 def get_global_contexte(self):
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,...)
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)