1 #@ MODIF N_MACRO_ETAPE Noyau DATE 14/09/2004 AUTEUR MCOURTOI M.COURTOIS
2 # -*- coding: iso-8859-1 -*-
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 # ======================================================================
25 Ce module contient la classe MACRO_ETAPE qui sert à vérifier et à exécuter
30 import types,sys,string
36 from N_Exception import AsException
38 from N_utils import AsType
40 class MACRO_ETAPE(N_ETAPE.ETAPE):
45 def __init__(self,oper=None,reuse=None,args={}):
49 - definition : objet portant les attributs de définition d'une étape
50 de type macro-commande. Il est initialisé par
53 - reuse : indique le concept d'entrée réutilisé. Il se trouvera donc
54 en sortie si les conditions d'exécution de l'opérateur
57 - valeur : arguments d'entrée de type mot-clé=valeur. Initialisé
65 self.parent=CONTEXT.get_current_step()
68 self.idracine=oper.label
69 self.appel=N_utils.callee_where()
73 self.current_context={}
74 self.index_etape_courante=0
77 # Dans le cas d'une macro écrite en Python, l'attribut Outputs est un
78 # dictionnaire qui contient les concepts produits de sortie
79 # (nom : ASSD) déclarés dans la fonction sd_prod
86 def make_register(self):
88 Initialise les attributs jdc, id, niveau et réalise les enregistrements
92 self.jdc = self.parent.get_jdc_root()
93 self.id=self.parent.register(self)
96 self.jdc = self.parent =None
100 def Build_sd(self,nom):
102 Construit le concept produit de l'opérateur. Deux cas
103 peuvent se présenter :
105 - le parent n'est pas défini. Dans ce cas, l'étape prend en charge
106 la création et le nommage du concept.
108 - le parent est défini. Dans ce cas, l'étape demande au parent la
109 création et le nommage du concept.
112 if not self.isactif():return
115 # On positionne la macro self en tant que current_step pour que les
116 # étapes créées lors de l'appel à sd_prod et à op_init aient la macro
118 self.set_current_step()
120 sd= self.parent.create_sdprod(self,nom)
121 if type(self.definition.op_init) == types.FunctionType:
122 apply(self.definition.op_init,(self,self.parent.g_context))
124 sd=self.get_sd_prod()
125 if sd != None and self.reuse == None:
126 # On ne nomme le concept que dans le cas de non reutilisation
129 self.reset_current_step()
130 except AsException,e:
131 self.reset_current_step()
132 raise AsException("Etape ",self.nom,'ligne : ',self.appel[0],
133 'fichier : ',self.appel[1],e)
134 except (EOFError,self.jdc.UserError):
135 # Le retablissement du step courant n'est pas strictement necessaire. On le fait pour des raisons de coherence
136 self.reset_current_step()
139 self.reset_current_step()
140 l=traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2])
141 raise AsException("Etape ",self.nom,'ligne : ',self.appel[0],
142 'fichier : ',self.appel[1]+'\n',
148 def get_sd_prod(self):
150 Retourne le concept résultat d'une macro étape
151 La difference avec une etape ou une proc-etape tient a ce que
152 le concept produit peut exister ou pas
153 Si sd_prod == None le concept produit n existe pas on retourne None
155 cas 1 : sd_prod n'est pas une fonction
156 il s'agit d'une sous classe de ASSD
157 on construit le sd à partir de cette classe
159 cas 2 : sd_prod est une fonction
160 on l'évalue avec les mots-clés de l'étape (mc_liste)
161 on construit le sd à partir de la classe obtenue
164 sd_prod=self.definition.sd_prod
166 if type(self.definition.sd_prod) == types.FunctionType:
167 d=self.cree_dict_valeurs(self.mc_liste)
169 # la sd_prod d'une macro a l'objet macro_etape lui meme en premier argument
170 # Comme sd_prod peut invoquer la méthode type_sdprod qui ajoute
171 # les concepts produits dans self.sdprods, il faut le mettre à zéro avant de l'appeler
173 sd_prod= apply(sd_prod,(self,),d)
174 except (EOFError,self.jdc.UserError):
177 if CONTEXT.debug: traceback.print_exc()
178 l=traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2])
179 raise AsException("impossible d affecter un type au resultat\n",string.join(l[2:]))
181 # on teste maintenant si la SD est réutilisée ou s'il faut la créer
182 if self.definition.reentrant != 'n' and self.reuse:
183 # Le concept produit est specifie reutilise (reuse=xxx). C'est une erreur mais non fatale.
184 # Elle sera traitee ulterieurement.
190 self.sd= sd_prod(etape=self)
192 # Si la commande est obligatoirement reentrante et reuse n'a pas ete specifie, c'est une erreur.
193 # On ne fait rien ici. L'erreur sera traitee par la suite.
196 def get_type_produit(self,force=0):
198 Retourne le type du concept résultat de l'étape et eventuellement type
199 les concepts produits "à droite" du signe égal (en entrée)
201 cas 1 : sd_prod de oper n'est pas une fonction
202 il s'agit d'une sous classe de ASSD
203 on retourne le nom de la classe
204 cas 2 : il s'agit d'une fonction
205 on l'évalue avec les mots-clés de l'étape (mc_liste)
206 et on retourne son résultat
208 if not force and hasattr(self,'typret'): return self.typret
209 if type(self.definition.sd_prod) == types.FunctionType:
210 d=self.cree_dict_valeurs(self.mc_liste)
212 # Comme sd_prod peut invoquer la méthode type_sdprod qui ajoute
213 # les concepts produits dans self.sdprods, il faut le mettre à zéro
215 sd_prod= apply(self.definition.sd_prod,(self,),d)
217 #traceback.print_exc()
220 sd_prod=self.definition.sd_prod
223 def get_contexte_avant(self,etape):
225 Retourne le dictionnaire des concepts connus avant etape
226 pour les commandes internes a la macro
227 On tient compte des commandes qui modifient le contexte
228 comme DETRUIRE ou les macros
230 # L'étape courante pour laquelle le contexte a été calculé est
231 # mémorisée dans self.index_etape_courante
232 # Si on insère des commandes (par ex, dans EFICAS), il faut
233 # préalablement remettre ce pointeur à 0
235 index_etape=self.etapes.index(etape)
237 index_etape=len(self.etapes)
239 if index_etape >= self.index_etape_courante:
240 # On calcule le contexte en partant du contexte existant
241 d=self.current_context
242 liste_etapes=self.etapes[self.index_etape_courante:index_etape]
244 d=self.current_context={}
245 liste_etapes=self.etapes
247 for e in liste_etapes:
252 self.index_etape_courante=index_etape
257 Méthode qui supprime toutes les références arrières afin que
258 l'objet puisse etre correctement détruit par le garbage collector
260 N_MCCOMPO.MCCOMPO.supprime(self)
263 if self.sd : self.sd.supprime()
264 for concept in self.sdprods:
266 for etape in self.etapes:
269 def type_sdprod(self,co,t):
271 Cette methode a pour fonction de typer le concept co avec le type t
272 dans les conditions suivantes
273 1- co est un concept produit de self
274 2- co est un concept libre : on le type et on l attribue à self
275 Elle enregistre egalement les concepts produits (on fait l hypothese
276 que la liste sdprods a été correctement initialisee, vide probablement)
278 if not hasattr(co,'etape'):
279 # Le concept vaut None probablement. On ignore l'appel
283 # le concept est libre
286 self.sdprods.append(co)
287 elif co.etape== self:
288 # le concept est produit par self
290 self.sdprods.append(co)
291 elif co.etape== self.parent:
292 # le concept est produit par la macro superieure
293 # on transfere la propriete
294 # On verifie que le type du concept existant co.__class__ est un sur type de celui attendu
295 # Cette règle est normalement cohérente avec les règles de vérification des mots-clés
296 if not issubclass(t,co.__class__):
297 raise AsException("Le type du concept produit %s devrait etre une sur classe de %s" %(co.__class__,t))
300 self.sdprods.append(co)
301 elif self.issubstep(co.etape):
302 # Le concept est propriété d'une sous etape de self. Il doit etre considere
303 # comme produit par la macro => ajout dans self.sdprods
304 self.sdprods.append(co)
306 # le concept est produit par une autre étape
309 def issubstep(self,etape):
311 Cette methode retourne un entier indiquant si etape est une
312 sous etape de la macro self ou non
316 if etape in self.etapes:return 1
317 for etap in self.etapes:
318 if etap.issubstep(etape):return 1
321 def register(self,etape):
323 Enregistrement de etape dans le contexte de la macro : liste etapes
324 et demande d enregistrement global aupres du JDC
326 self.etapes.append(etape)
327 idetape=self.jdc.g_register(etape)
332 Methode appelee dans l __init__ d un ASSD a sa creation pour
333 s enregistrer (reserve aux ASSD créés au sein d'une MACRO)
336 return self.jdc.o_register(sd)
338 def create_sdprod(self,etape,nomsd):
340 Intention : Cette methode doit fabriquer le concept produit retourne
341 par l'etape etape et le nommer.
342 Elle est appelée à l'initiative de l'etape
343 pendant le processus de construction de cette etape : methode __call__
344 de la classe CMD (OPER ou MACRO)
345 Ce travail est réalisé par le contexte supérieur (etape.parent)
346 car dans certains cas, le concept ne doit pas etre fabriqué mais
347 l'etape doit simplement utiliser un concept préexistant.
348 Cas 1 : etape.reuse != None : le concept est réutilisé
349 Cas 2 : l'étape appartient à une macro qui a déclaré un concept
350 de sortie qui doit etre produit par cette etape.
352 if self.Outputs.has_key(nomsd):
353 # Il s'agit d'un concept de sortie de la macro. Il ne faut pas le créer
354 # Il faut quand meme appeler la fonction sd_prod si elle existe.
355 # get_type_produit le fait et donne le type attendu par la commande pour verification ultérieure.
356 sdprod=etape.get_type_produit()
357 sd=self.Outputs[nomsd]
358 # On verifie que le type du concept existant sd.__class__ est un sur type de celui attendu
359 # Cette règle est normalement cohérente avec les règles de vérification des mots-clés
360 if not issubclass(sdprod,sd.__class__):
361 raise AsException("Le type du concept produit %s devrait etre une sur classe de %s" %(sd.__class__,sdprod))
362 # La propriete du concept est transferee a l'etape avec le type attendu par l'étape
365 # On donne au concept le type produit par la sous commande.
366 # Le principe est le suivant : apres avoir verifie que le type deduit par la sous commande
367 # est bien coherent avec celui initialement affecte par la macro (voir ci dessus)
368 # on affecte au concept ce type car il peut etre plus precis (derive, en general)
370 # On force également le nom stocké dans l'attribut sdnom : on lui donne le nom
371 # du concept associé à nomsd
373 elif etape.definition.reentrant != 'n' and etape.reuse != None:
374 # On est dans le cas d'une commande avec reutilisation d'un concept existant
375 # get_sd_prod fait le necessaire : verifications, associations, etc. mais ne cree
376 # pas un nouveau concept. Il retourne le concept reutilise
377 sd= etape.get_sd_prod()
378 # Dans le cas d'un concept nomme automatiquement : _xxx, __xxx,
379 # On force le nom stocke dans l'attribut sdnom de l'objet etape : on lui donne le nom
380 # du concept reutilise (sd ou etape.reuse c'est pareil)
381 # Ceci est indispensable pour eviter des erreurs lors des verifications des macros
382 # En effet une commande avec reutilisation d'un concept verifie que le nom de
383 # la variable a gauche du signe = est le meme que celui du concept reutilise.
384 # Lorsqu'une telle commande apparait dans une macro, on supprime cette verification.
385 if (etape.sdnom == '' or etape.sdnom[0] == '_'):
388 # On est dans le cas de la creation d'un nouveau concept
389 sd= etape.get_sd_prod()
391 self.NommerSdprod(sd,nomsd)
394 def NommerSdprod(self,sd,sdnom,restrict='non'):
396 Cette methode est appelee par les etapes internes de la macro
397 La macro appelle le JDC pour valider le nommage
398 On considere que l espace de nom est unique et géré par le JDC
399 Si le nom est deja utilise, l appel leve une exception
400 Si restrict=='non', on insere le concept dans le contexte de la macro
401 Si restrict=='oui', on n'insere pas le concept dans le contexte de la macro
403 # Normalement, lorsqu'on appelle cette methode, on ne veut nommer que des concepts nouvellement crees.
404 # Le filtrage sur les concepts a creer ou a ne pas creer est fait dans la methode
405 # create_sdprod. La seule chose a verifier apres conversion eventuelle du nom
406 # est de verifier que le nom n'est pas deja attribue. Ceci est fait en delegant
407 # au JDC par l'intermediaire du parent.
409 #XXX attention inconsistence : prefix et gcncon ne sont pas
410 # définis dans le package Noyau. La methode NommerSdprod pour
411 # les macros devrait peut etre etre déplacée dans Build ???
413 if CONTEXT.debug : print "MACRO.NommerSdprod: ",sd,sdnom
415 if hasattr(self,'prefix'):
416 # Dans le cas de l'include_materiau on ajoute un prefixe au nom du concept
417 if sdnom != self.prefix:sdnom=self.prefix+sdnom
419 if self.Outputs.has_key(sdnom):
420 # Il s'agit d'un concept de sortie de la macro produit par une sous commande
421 sdnom=self.Outputs[sdnom].nom
422 elif sdnom != '' and sdnom[0] == '_':
423 # Si le nom du concept commence par le caractere _ on lui attribue
424 # un identificateur JEVEUX construit par gcncon et respectant
425 # la regle gcncon legerement adaptee ici
426 # nom commencant par __ : il s'agit de concepts qui seront detruits
427 # nom commencant par _ : il s'agit de concepts intermediaires qui seront gardes
428 # ATTENTION : il faut traiter différemment les concepts dont le nom
429 # commence par _ mais qui sont des concepts nommés automatiquement par
430 # une éventuelle sous macro.
431 # Le test suivant n'est pas tres rigoureux mais permet de fonctionner pour le moment (a améliorer)
432 if sdnom[1] in string.digits:
433 # Ce concept provient probablement d'une macro appelee par self
435 elif sdnom[1] == '_':
436 sdnom=self.gcncon('.')
438 sdnom=self.gcncon('_')
440 # On est dans le cas d'un nom de concept global.
443 if restrict == 'non':
444 # On demande le nommage au parent mais sans ajout du concept dans le contexte du parent
445 # car on va l'ajouter dans le contexte de la macro
446 self.parent.NommerSdprod(sd,sdnom,restrict='oui')
447 # On ajoute dans le contexte de la macro les concepts nommes
448 # Ceci est indispensable pour les CO (macro) dans un INCLUDE
449 self.g_context[sdnom]=sd
451 # La demande de nommage vient probablement d'une macro qui a mis
452 # le concept dans son contexte. On ne traite plus que le nommage (restrict="oui")
453 self.parent.NommerSdprod(sd,sdnom,restrict='oui')
455 def delete_concept_after_etape(self,etape,sd):
457 Met à jour les étapes de la MACRO qui sont après etape suite à
458 la disparition du concept sd
460 # Cette methode est définie dans le noyau mais ne sert que pendant la phase de creation
461 # des etapes et des concepts. Il n'y a aucun traitement particulier à réaliser
462 # Dans d'autres conditions, il faudrait surcharger cette méthode.
465 def accept(self,visitor):
467 Cette methode permet de parcourir l'arborescence des objets
468 en utilisant le pattern VISITEUR
470 visitor.visitMACRO_ETAPE(self)
472 def update_context(self,d):
474 Met à jour le contexte contenu dans le dictionnaire d
475 Une MACRO_ETAPE peut ajouter plusieurs concepts dans le contexte
476 Une fonction enregistree dans op_init peut egalement modifier le contexte
478 if type(self.definition.op_init) == types.FunctionType:
479 apply(self.definition.op_init,(self,d))
480 if self.sd != None:d[self.sd.nom]=self.sd
481 for co in self.sdprods:
484 def make_include(self,unite=None):
486 Inclut un fichier dont l'unite logique est unite
488 if not unite : return
489 f,text=self.get_file(unite=unite,fic_origine=self.parent.nom)
490 self.fichier_init = f
492 self.make_contexte(f,text)
494 def make_poursuite(self):
496 Inclut un fichier poursuite
499 f,text=self.get_file(fic_origine=self.parent.nom)
501 raise AsException("Impossible d'ouvrir la base pour une poursuite")
504 self.make_contexte(f,text)
506 def make_contexte(self,f,text):
508 Interprete le texte fourni (text) issu du fichier f
509 dans le contexte du parent.
510 Cette methode est utile pour le fonctionnement des
513 # on execute le texte fourni dans le contexte forme par
514 # le contexte de l etape pere (global au sens Python)
515 # et le contexte de l etape (local au sens Python)
516 code=compile(text,f,'exec')
519 self.contexte_fichier_init = d
520 globs=self.parent.get_global_contexte()
523 def get_global_contexte(self):
525 Cette methode retourne le contexte global fourni
526 par le parent(self) a une etape fille (l'appelant) pour
527 realiser des evaluations de texte Python (INCLUDE,...)
529 # Le contexte global est forme par concatenation du contexte
530 # du parent de self et de celui de l'etape elle meme (self)
531 d=self.parent.get_global_contexte()
532 d.update(self.g_context)
536 """ Méthode qui retourne une copie de self non enregistrée auprès du JDC
538 On surcharge la methode de ETAPE pour exprimer que les concepts crees
539 par la MACRO d'origine ne sont pas crees par la copie mais eventuellement
542 etape=N_ETAPE.ETAPE.copy(self)
546 def copy_intern(self,etape):
547 """ Cette méthode effectue la recopie des etapes internes d'une macro
548 passée en argument (etape)
551 for etp in etape.etapes:
553 new_etp.copy_reuse(etp)
554 new_etp.copy_sdnom(etp)
555 new_etp.reparent(self)
557 new_sd = etp.sd.__class__(etape=new_etp)
560 new_sd.nom = etp.sd.nom
562 self.NommerSdprod(new_sd,etp.sd.nom)
563 new_etp.copy_intern(etp)
564 self.etapes.append(new_etp)