1 # -*- coding: utf-8 -*-
2 #@ MODIF N_MACRO_ETAPE Noyau DATE 16/03/2004 AUTEUR GNICOLAS G.NICOLAS
3 # CONFIGURATION MANAGEMENT OF EDF VERSION
4 # ======================================================================
5 # COPYRIGHT (C) 1991 - 2002 EDF R&D WWW.CODE-ASTER.ORG
6 # THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
7 # IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
8 # THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR
9 # (AT YOUR OPTION) ANY LATER VERSION.
11 # THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT
12 # WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF
13 # MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU
14 # GENERAL PUBLIC LICENSE FOR MORE DETAILS.
16 # YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE
17 # ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,
18 # 1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.
21 # ======================================================================
23 Ce module contient la classe MACRO_ETAPE qui sert à vérifier et à exécuter
28 import types,sys,string
34 from N_Exception import AsException
36 from N_utils import AsType
38 class MACRO_ETAPE(N_ETAPE.ETAPE):
43 def __init__(self,oper=None,reuse=None,args={}):
47 - definition : objet portant les attributs de définition d'une étape
48 de type macro-commande. Il est initialisé par
51 - reuse : indique le concept d'entrée réutilisé. Il se trouvera donc
52 en sortie si les conditions d'exécution de l'opérateur
55 - valeur : arguments d'entrée de type mot-clé=valeur. Initialisé
63 self.parent=CONTEXT.get_current_step()
66 self.idracine=oper.label
67 self.appel=N_utils.callee_where()
71 self.current_context={}
72 self.index_etape_courante=0
75 # Dans le cas d'une macro écrite en Python, l'attribut Outputs est un
76 # dictionnaire qui contient les concepts produits de sortie
77 # (nom : ASSD) déclarés dans la fonction sd_prod
84 def make_register(self):
86 Initialise les attributs jdc, id, niveau et réalise les enregistrements
90 self.jdc = self.parent.get_jdc_root()
91 self.id=self.parent.register(self)
94 self.jdc = self.parent =None
98 def Build_sd(self,nom):
100 Construit le concept produit de l'opérateur. Deux cas
101 peuvent se présenter :
103 - le parent n'est pas défini. Dans ce cas, l'étape prend en charge
104 la création et le nommage du concept.
106 - le parent est défini. Dans ce cas, l'étape demande au parent la
107 création et le nommage du concept.
110 if not self.isactif():return
113 # On positionne la macro self en tant que current_step pour que les
114 # étapes créées lors de l'appel à sd_prod et à op_init aient la macro
116 self.set_current_step()
118 sd= self.parent.create_sdprod(self,nom)
119 if type(self.definition.op_init) == types.FunctionType:
120 apply(self.definition.op_init,(self,self.parent.g_context))
122 sd=self.get_sd_prod()
123 if sd != None and self.reuse == None:
124 # On ne nomme le concept que dans le cas de non reutilisation
127 self.reset_current_step()
128 except AsException,e:
129 self.reset_current_step()
130 raise AsException("Etape ",self.nom,'ligne : ',self.appel[0],
131 'fichier : ',self.appel[1],e)
132 except (EOFError,self.jdc.UserError):
133 # Le retablissement du step courant n'est pas strictement necessaire. On le fait pour des raisons de coherence
134 self.reset_current_step()
137 self.reset_current_step()
138 l=traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2])
139 raise AsException("Etape ",self.nom,'ligne : ',self.appel[0],
140 'fichier : ',self.appel[1]+'\n',
146 def get_sd_prod(self):
148 Retourne le concept résultat d'une macro étape
149 La difference avec une etape ou une proc-etape tient a ce que
150 le concept produit peut exister ou pas
151 Si sd_prod == None le concept produit n existe pas on retourne None
153 cas 1 : sd_prod n'est pas une fonction
154 il s'agit d'une sous classe de ASSD
155 on construit le sd à partir de cette classe
157 cas 2 : sd_prod est une fonction
158 on l'évalue avec les mots-clés de l'étape (mc_liste)
159 on construit le sd à partir de la classe obtenue
162 sd_prod=self.definition.sd_prod
164 if type(self.definition.sd_prod) == types.FunctionType:
165 d=self.cree_dict_valeurs(self.mc_liste)
167 # la sd_prod d'une macro a l'objet macro_etape lui meme en premier argument
168 # Comme sd_prod peut invoquer la méthode type_sdprod qui ajoute
169 # les concepts produits dans self.sdprods, il faut le mettre à zéro avant de l'appeler
171 sd_prod= apply(sd_prod,(self,),d)
172 except (EOFError,self.jdc.UserError):
175 if CONTEXT.debug: traceback.print_exc()
176 l=traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2])
177 raise AsException("impossible d affecter un type au resultat\n",string.join(l[2:]))
179 # on teste maintenant si la SD est réutilisée ou s'il faut la créer
180 if self.definition.reentrant != 'n' and self.reuse:
181 # Le concept produit est specifie reutilise (reuse=xxx). C'est une erreur mais non fatale.
182 # Elle sera traitee ulterieurement.
188 self.sd= sd_prod(etape=self)
190 # Si la commande est obligatoirement reentrante et reuse n'a pas ete specifie, c'est une erreur.
191 # On ne fait rien ici. L'erreur sera traitee par la suite.
194 def get_type_produit(self,force=0):
196 Retourne le type du concept résultat de l'étape et eventuellement type
197 les concepts produits "à droite" du signe égal (en entrée)
199 cas 1 : sd_prod de oper n'est pas une fonction
200 il s'agit d'une sous classe de ASSD
201 on retourne le nom de la classe
202 cas 2 : il s'agit d'une fonction
203 on l'évalue avec les mots-clés de l'étape (mc_liste)
204 et on retourne son résultat
206 if not force and hasattr(self,'typret'): return self.typret
207 if type(self.definition.sd_prod) == types.FunctionType:
208 d=self.cree_dict_valeurs(self.mc_liste)
210 # Comme sd_prod peut invoquer la méthode type_sdprod qui ajoute
211 # les concepts produits dans self.sdprods, il faut le mettre à zéro
213 sd_prod= apply(self.definition.sd_prod,(self,),d)
215 #traceback.print_exc()
218 sd_prod=self.definition.sd_prod
221 def get_contexte_avant(self,etape):
223 Retourne le dictionnaire des concepts connus avant etape
224 pour les commandes internes a la macro
225 On tient compte des commandes qui modifient le contexte
226 comme DETRUIRE ou les macros
228 # L'étape courante pour laquelle le contexte a été calculé est
229 # mémorisée dans self.index_etape_courante
230 # Si on insère des commandes (par ex, dans EFICAS), il faut
231 # préalablement remettre ce pointeur à 0
233 index_etape=self.etapes.index(etape)
235 index_etape=len(self.etapes)
237 if index_etape >= self.index_etape_courante:
238 # On calcule le contexte en partant du contexte existant
239 d=self.current_context
240 liste_etapes=self.etapes[self.index_etape_courante:index_etape]
242 d=self.current_context={}
243 liste_etapes=self.etapes
245 for e in liste_etapes:
250 self.index_etape_courante=index_etape
255 Méthode qui supprime toutes les références arrières afin que
256 l'objet puisse etre correctement détruit par le garbage collector
258 N_MCCOMPO.MCCOMPO.supprime(self)
261 if self.sd : self.sd.supprime()
262 for concept in self.sdprods:
264 for etape in self.etapes:
267 def type_sdprod(self,co,t):
269 Cette methode a pour fonction de typer le concept co avec le type t
270 dans les conditions suivantes
271 1- co est un concept produit de self
272 2- co est un concept libre : on le type et on l attribue à self
273 Elle enregistre egalement les concepts produits (on fait l hypothese
274 que la liste sdprods a été correctement initialisee, vide probablement)
276 if not hasattr(co,'etape'):
277 # Le concept vaut None probablement. On ignore l'appel
281 # le concept est libre
284 self.sdprods.append(co)
285 elif co.etape== self:
286 # le concept est produit par self
288 self.sdprods.append(co)
289 elif co.etape== self.parent:
290 # le concept est produit par la macro superieure
291 # on transfere la propriete
292 # On verifie que le type du concept existant co.__class__ est un sur type de celui attendu
293 # Cette règle est normalement cohérente avec les règles de vérification des mots-clés
294 if not issubclass(t,co.__class__):
295 raise AsException("Le type du concept produit %s devrait etre une sur classe de %s" %(co.__class__,t))
298 self.sdprods.append(co)
299 elif self.issubstep(co.etape):
300 # Le concept est propriété d'une sous etape de self. Il doit etre considere
301 # comme produit par la macro => ajout dans self.sdprods
302 self.sdprods.append(co)
304 # le concept est produit par une autre étape
307 def issubstep(self,etape):
309 Cette methode retourne un entier indiquant si etape est une
310 sous etape de la macro self ou non
314 if etape in self.etapes:return 1
315 for etap in self.etapes:
316 if etap.issubstep(etape):return 1
319 def register(self,etape):
321 Enregistrement de etape dans le contexte de la macro : liste etapes
322 et demande d enregistrement global aupres du JDC
324 self.etapes.append(etape)
325 idetape=self.jdc.g_register(etape)
330 Methode appelee dans l __init__ d un ASSD a sa creation pour
331 s enregistrer (reserve aux ASSD créés au sein d'une MACRO)
334 return self.jdc.o_register(sd)
336 def create_sdprod(self,etape,nomsd):
338 Intention : Cette methode doit fabriquer le concept produit retourne
339 par l'etape etape et le nommer.
340 Elle est appelée à l'initiative de l'etape
341 pendant le processus de construction de cette etape : methode __call__
342 de la classe CMD (OPER ou MACRO)
343 Ce travail est réalisé par le contexte supérieur (etape.parent)
344 car dans certains cas, le concept ne doit pas etre fabriqué mais
345 l'etape doit simplement utiliser un concept préexistant.
346 Cas 1 : etape.reuse != None : le concept est réutilisé
347 Cas 2 : l'étape appartient à une macro qui a déclaré un concept
348 de sortie qui doit etre produit par cette etape.
350 if self.Outputs.has_key(nomsd):
351 # Il s'agit d'un concept de sortie de la macro. Il ne faut pas le créer
352 # Il faut quand meme appeler la fonction sd_prod si elle existe.
353 # get_type_produit le fait et donne le type attendu par la commande pour verification ultérieure.
354 sdprod=etape.get_type_produit()
355 sd=self.Outputs[nomsd]
356 # On verifie que le type du concept existant sd.__class__ est un sur type de celui attendu
357 # Cette règle est normalement cohérente avec les règles de vérification des mots-clés
358 if not issubclass(sdprod,sd.__class__):
359 raise AsException("Le type du concept produit %s devrait etre une sur classe de %s" %(sd.__class__,sdprod))
360 # La propriete du concept est transferee a l'etape avec le type attendu par l'étape
363 # On donne au concept le type produit par la sous commande.
364 # Le principe est le suivant : apres avoir verifie que le type deduit par la sous commande
365 # est bien coherent avec celui initialement affecte par la macro (voir ci dessus)
366 # on affecte au concept ce type car il peut etre plus precis (derive, en general)
368 # On force également le nom stocké dans l'attribut sdnom : on lui donne le nom
369 # du concept associé à nomsd
371 elif etape.definition.reentrant != 'n' and etape.reuse != None:
372 # On est dans le cas d'une commande avec reutilisation d'un concept existant
373 # get_sd_prod fait le necessaire : verifications, associations, etc. mais ne cree
374 # pas un nouveau concept. Il retourne le concept reutilise
375 sd= etape.get_sd_prod()
376 # Dans le cas d'un concept nomme automatiquement : _xxx, __xxx,
377 # On force le nom stocke dans l'attribut sdnom de l'objet etape : on lui donne le nom
378 # du concept reutilise (sd ou etape.reuse c'est pareil)
379 # Ceci est indispensable pour eviter des erreurs lors des verifications des macros
380 # En effet une commande avec reutilisation d'un concept verifie que le nom de
381 # la variable a gauche du signe = est le meme que celui du concept reutilise.
382 # Lorsqu'une telle commande apparait dans une macro, on supprime cette verification.
383 if (etape.sdnom == '' or etape.sdnom[0] == '_'):
386 # On est dans le cas de la creation d'un nouveau concept
387 sd= etape.get_sd_prod()
389 self.NommerSdprod(sd,nomsd)
392 def NommerSdprod(self,sd,sdnom,restrict='non'):
394 Cette methode est appelee par les etapes internes de la macro
395 La macro appelle le JDC pour valider le nommage
396 On considere que l espace de nom est unique et géré par le JDC
397 Si le nom est deja utilise, l appel leve une exception
398 Si restrict=='non', on insere le concept dans le contexte de la macro
399 Si restrict=='oui', on n'insere pas le concept dans le contexte de la macro
401 # Normalement, lorsqu'on appelle cette methode, on ne veut nommer que des concepts nouvellement crees.
402 # Le filtrage sur les concepts a creer ou a ne pas creer est fait dans la methode
403 # create_sdprod. La seule chose a verifier apres conversion eventuelle du nom
404 # est de verifier que le nom n'est pas deja attribue. Ceci est fait en delegant
405 # au JDC par l'intermediaire du parent.
407 #XXX attention inconsistence : prefix et gcncon ne sont pas
408 # définis dans le package Noyau. La methode NommerSdprod pour
409 # les macros devrait peut etre etre déplacée dans Build ???
411 if CONTEXT.debug : print "MACRO.NommerSdprod: ",sd,sdnom
413 if hasattr(self,'prefix'):
414 # Dans le cas de l'include_materiau on ajoute un prefixe au nom du concept
415 if sdnom != self.prefix:sdnom=self.prefix+sdnom
417 if self.Outputs.has_key(sdnom):
418 # Il s'agit d'un concept de sortie de la macro produit par une sous commande
419 sdnom=self.Outputs[sdnom].nom
420 elif sdnom != '' and sdnom[0] == '_':
421 # Si le nom du concept commence par le caractere _ on lui attribue
422 # un identificateur JEVEUX construit par gcncon et respectant
423 # la regle gcncon legerement adaptee ici
424 # nom commencant par __ : il s'agit de concepts qui seront detruits
425 # nom commencant par _ : il s'agit de concepts intermediaires qui seront gardes
426 # ATTENTION : il faut traiter différemment les concepts dont le nom
427 # commence par _ mais qui sont des concepts nommés automatiquement par
428 # une éventuelle sous macro.
429 # Le test suivant n'est pas tres rigoureux mais permet de fonctionner pour le moment (a améliorer)
430 if sdnom[1] in string.digits:
431 # Ce concept provient probablement d'une macro appelee par self
433 elif sdnom[1] == '_':
434 sdnom=self.gcncon('.')
436 sdnom=self.gcncon('_')
438 # On est dans le cas d'un nom de concept global.
441 if restrict == 'non':
442 # On demande le nommage au parent mais sans ajout du concept dans le contexte du parent
443 # car on va l'ajouter dans le contexte de la macro
444 self.parent.NommerSdprod(sd,sdnom,restrict='oui')
445 # On ajoute dans le contexte de la macro les concepts nommes
446 # Ceci est indispensable pour les CO (macro) dans un INCLUDE
447 self.g_context[sdnom]=sd
449 # La demande de nommage vient probablement d'une macro qui a mis
450 # le concept dans son contexte. On ne traite plus que le nommage (restrict="oui")
451 self.parent.NommerSdprod(sd,sdnom,restrict='oui')
453 def delete_concept_after_etape(self,etape,sd):
455 Met à jour les étapes de la MACRO qui sont après etape suite à
456 la disparition du concept sd
458 # Cette methode est définie dans le noyau mais ne sert que pendant la phase de creation
459 # des etapes et des concepts. Il n'y a aucun traitement particulier à réaliser
460 # Dans d'autres conditions, il faudrait surcharger cette méthode.
463 def accept(self,visitor):
465 Cette methode permet de parcourir l'arborescence des objets
466 en utilisant le pattern VISITEUR
468 visitor.visitMACRO_ETAPE(self)
470 def update_context(self,d):
472 Met à jour le contexte contenu dans le dictionnaire d
473 Une MACRO_ETAPE peut ajouter plusieurs concepts dans le contexte
474 Une fonction enregistree dans op_init peut egalement modifier le contexte
476 if type(self.definition.op_init) == types.FunctionType:
477 apply(self.definition.op_init,(self,d))
478 if self.sd != None:d[self.sd.nom]=self.sd
479 for co in self.sdprods:
482 def make_include(self,unite=None):
484 Inclut un fichier dont l'unite logique est unite
486 if not unite : return
487 f,text=self.get_file(unite=unite,fic_origine=self.parent.nom)
488 self.fichier_init = f
490 self.make_contexte(f,text)
492 def make_poursuite(self):
494 Inclut un fichier poursuite
497 f,text=self.get_file(fic_origine=self.parent.nom)
499 raise AsException("Impossible d'ouvrir la base pour une poursuite")
502 self.make_contexte(f,text)
504 def make_contexte(self,f,text):
506 Interprete le texte fourni (text) issu du fichier f
507 dans le contexte du parent.
508 Cette methode est utile pour le fonctionnement des
511 # on execute le texte fourni dans le contexte forme par
512 # le contexte de l etape pere (global au sens Python)
513 # et le contexte de l etape (local au sens Python)
514 code=compile(text,f,'exec')
517 self.contexte_fichier_init = d
518 globs=self.parent.get_global_contexte()
521 def get_global_contexte(self):
523 Cette methode retourne le contexte global fourni
524 par le parent(self) a une etape fille (l'appelant) pour
525 realiser des evaluations de texte Python (INCLUDE,...)
527 # Le contexte global est forme par concatenation du contexte
528 # du parent de self et de celui de l'etape elle meme (self)
529 d=self.parent.get_global_contexte()
530 d.update(self.g_context)
534 """ Méthode qui retourne une copie de self non enregistrée auprès du JDC
536 On surcharge la methode de ETAPE pour exprimer que les concepts crees
537 par la MACRO d'origine ne sont pas crees par la copie mais eventuellement
540 etape=N_ETAPE.ETAPE.copy(self)
544 def copy_intern(self,etape):
545 """ Cette méthode effectue la recopie des etapes internes d'une macro
546 passée en argument (etape)
549 for etp in etape.etapes:
551 new_etp.copy_reuse(etp)
552 new_etp.copy_sdnom(etp)
553 new_etp.reparent(self)
555 new_sd = etp.sd.__class__(etape=new_etp)
558 new_sd.nom = etp.sd.nom
560 self.NommerSdprod(new_sd,etp.sd.nom)
561 new_etp.copy_intern(etp)
562 self.etapes.append(new_etp)