1 #@ MODIF N_MACRO_ETAPE Noyau DATE 16/05/2007 AUTEUR COURTOIS 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 from N_ASSD import ASSD
42 class MACRO_ETAPE(N_ETAPE.ETAPE):
48 def __init__(self,oper=None,reuse=None,args={}):
52 - definition : objet portant les attributs de définition d'une étape
53 de type macro-commande. Il est initialisé par
56 - reuse : indique le concept d'entrée réutilisé. Il se trouvera donc
57 en sortie si les conditions d'exécution de l'opérateur
60 - valeur : arguments d'entrée de type mot-clé=valeur. Initialisé
68 self.parent=CONTEXT.get_current_step()
71 self.idracine=oper.label
72 self.appel=N_utils.callee_where()
76 self.current_context={}
77 self.index_etape_courante=0
80 # Dans le cas d'une macro écrite en Python, l'attribut Outputs est un
81 # dictionnaire qui contient les concepts produits de sortie
82 # (nom : ASSD) déclarés dans la fonction sd_prod
88 self.UserError="UserError"
90 def make_register(self):
92 Initialise les attributs jdc, id, niveau et réalise les enregistrements
96 self.jdc = self.parent.get_jdc_root()
97 self.id=self.parent.register(self)
99 self.UserError=self.jdc.UserError
101 self.jdc = self.parent =None
104 self.UserError="UserError"
106 def Build_sd(self,nom):
108 Construit le concept produit de l'opérateur. Deux cas
109 peuvent se présenter :
111 - le parent n'est pas défini. Dans ce cas, l'étape prend en charge
112 la création et le nommage du concept.
114 - le parent est défini. Dans ce cas, l'étape demande au parent la
115 création et le nommage du concept.
120 # On positionne la macro self en tant que current_step pour que les
121 # étapes créées lors de l'appel à sd_prod et à op_init aient la macro
123 self.set_current_step()
125 sd= self.parent.create_sdprod(self,nom)
126 if type(self.definition.op_init) == types.FunctionType:
127 apply(self.definition.op_init,(self,self.parent.g_context))
129 sd=self.get_sd_prod()
130 if sd != None and self.reuse == None:
131 # On ne nomme le concept que dans le cas de non reutilisation
134 self.reset_current_step()
135 except AsException,e:
136 self.reset_current_step()
137 raise AsException("Etape ",self.nom,'ligne : ',self.appel[0],
138 'fichier : ',self.appel[1],e)
139 except (EOFError,self.UserError):
140 # Le retablissement du step courant n'est pas strictement necessaire. On le fait pour des raisons de coherence
141 self.reset_current_step()
144 self.reset_current_step()
145 l=traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2])
146 raise AsException("Etape ",self.nom,'ligne : ',self.appel[0],
147 'fichier : ',self.appel[1]+'\n',
155 Marquage des concepts CO d'une macro-commande
157 # On marque les concepts CO pour verification ulterieure de leur bonne utilisation
160 #if not hasattr(c,"_etape") or c._etape is not c.etape:
164 def get_sd_prod(self):
166 Retourne le concept résultat d'une macro étape
167 La difference avec une etape ou une proc-etape tient a ce que
168 le concept produit peut exister ou pas
170 Si sd_prod == None le concept produit n existe pas on retourne None
173 - cas 1 : sd_prod n'est pas une fonction
174 il s'agit d'une sous classe de ASSD
175 on construit le sd à partir de cette classe
177 - cas 2 : sd_prod est une fonction
178 on l'évalue avec les mots-clés de l'étape (mc_liste)
179 on construit le sd à partir de la classe obtenue
182 sd_prod=self.definition.sd_prod
184 # On marque les concepts CO pour verification ulterieure de leur bonne utilisation
187 if type(self.definition.sd_prod) == types.FunctionType:
188 d=self.cree_dict_valeurs(self.mc_liste)
190 # la sd_prod d'une macro a l'objet macro_etape lui meme en premier argument
191 # Comme sd_prod peut invoquer la méthode type_sdprod qui ajoute
192 # les concepts produits dans self.sdprods, il faut le mettre à zéro avant de l'appeler
194 sd_prod= apply(sd_prod,(self,),d)
195 except (EOFError,self.UserError):
198 if CONTEXT.debug: traceback.print_exc()
199 l=traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2])
200 raise AsException("impossible d affecter un type au resultat\n",string.join(l[2:]))
202 # on teste maintenant si la SD est réutilisée ou s'il faut la créer
203 if self.definition.reentrant != 'n' and self.reuse:
204 # Le concept produit est specifie reutilise (reuse=xxx). C'est une erreur mais non fatale.
205 # Elle sera traitee ulterieurement.
211 self.sd= sd_prod(etape=self)
213 # Si la commande est obligatoirement reentrante et reuse n'a pas ete specifie, c'est une erreur.
214 # On ne fait rien ici. L'erreur sera traitee par la suite.
216 if self.sd is not None and not isinstance(self.sd, ASSD):
217 raise AsException("""
218 Impossible de typer le résultat !
220 Utilisateur : Soit la valeur fournie derrière "reuse" est incorrecte,
221 soit il y a une "," à la fin d'une commande précédente.
222 Développeur : La fonction "sd_prod" retourne un type invalide.""")
225 def get_type_produit(self,force=0):
227 return self.get_type_produit_brut(force)
229 #traceback.print_exc()
232 def get_type_produit_brut(self,force=0):
234 Retourne le type du concept résultat de l'étape et eventuellement type
235 les concepts produits "à droite" du signe égal (en entrée)
238 - cas 1 : sd_prod de oper n'est pas une fonction
239 il s'agit d'une sous classe de ASSD
240 on retourne le nom de la classe
241 - cas 2 : il s'agit d'une fonction
242 on l'évalue avec les mots-clés de l'étape (mc_liste)
243 et on retourne son résultat
245 if not force and hasattr(self,'typret'): return self.typret
246 # On marque les concepts CO pour verification ulterieure de leur bonne utilisation
249 if type(self.definition.sd_prod) == types.FunctionType:
250 d=self.cree_dict_valeurs(self.mc_liste)
251 # Comme sd_prod peut invoquer la méthode type_sdprod qui ajoute
252 # les concepts produits dans self.sdprods, il faut le mettre à zéro
254 sd_prod= apply(self.definition.sd_prod,(self,),d)
256 sd_prod=self.definition.sd_prod
259 def get_contexte_avant(self,etape):
261 Retourne le dictionnaire des concepts connus avant etape
262 pour les commandes internes a la macro
263 On tient compte des commandes qui modifient le contexte
264 comme DETRUIRE ou les macros
266 # L'étape courante pour laquelle le contexte a été calculé est
267 # mémorisée dans self.index_etape_courante
268 # Si on insère des commandes (par ex, dans EFICAS), il faut
269 # préalablement remettre ce pointeur à 0
271 index_etape=self.etapes.index(etape)
273 index_etape=len(self.etapes)
275 if index_etape >= self.index_etape_courante:
276 # On calcule le contexte en partant du contexte existant
277 d=self.current_context
278 liste_etapes=self.etapes[self.index_etape_courante:index_etape]
280 d=self.current_context={}
281 liste_etapes=self.etapes
283 for e in liste_etapes:
288 self.index_etape_courante=index_etape
293 Méthode qui supprime toutes les références arrières afin que
294 l'objet puisse etre correctement détruit par le garbage collector
296 N_MCCOMPO.MCCOMPO.supprime(self)
299 if self.sd : self.sd.supprime()
300 for concept in self.sdprods:
302 for etape in self.etapes:
305 def type_sdprod(self,co,t):
307 Cette methode a pour fonction de typer le concept co avec le type t
308 dans les conditions suivantes :
309 1. co est un concept produit de self
310 2. co est un concept libre : on le type et on l attribue à self
312 Elle enregistre egalement les concepts produits (on fait l hypothese
313 que la liste sdprods a été correctement initialisee, vide probablement)
315 if not hasattr(co,'etape'):
316 # Le concept vaut None probablement. On ignore l'appel
319 # On cherche a discriminer les differents cas de typage d'un concept
320 # produit par une macro qui est specifie dans un mot cle simple.
321 # On peut passer plusieurs fois par type_sdprod ce qui explique
322 # le nombre important de cas.
324 # Cas 1 : Le concept est libre. Il vient d'etre cree par CO(nom)
325 # Cas 2 : Le concept est produit par la macro. On est deja passe par type_sdprod.
326 # Cas semblable a Cas 1.
327 # Cas 3 : Le concept est produit par la macro englobante (parent). On transfere
328 # la propriete du concept de la macro parent a la macro courante (self)
329 # en verifiant que le type est valide
330 # Cas 4 : La concept est la propriete d'une etape fille. Ceci veut dire qu'on est
331 # deja passe par type_sdprod et que la propriete a ete transfere a une
332 # etape fille. Cas semblable a Cas 3.
333 # Cas 5 : Le concept est produit par une etape externe a la macro.
336 # Cas 1 : le concept est libre
337 # On l'attache a la macro et on change son type dans le type demande
338 # Recherche du mot cle simple associe au concept
339 mcs=self.get_mcs_with_co(co)
341 raise AsException("""Erreur interne.
342 Il ne devrait y avoir qu'un seul mot cle porteur du concept CO (%s)""" % co)
344 if not self.typeCO in mcs.definition.type:
345 raise AsException("""Erreur interne.
346 Impossible de changer le type du concept (%s). Le mot cle associe ne supporte pas CO mais seulement (%s)""" %(co,mcs.definition.type))
348 # affectation du bon type du concept et
349 # initialisation de sa partie "sd"
350 if CONTEXT.debug:print "changement de type:",co,t
353 self.sdprods.append(co)
355 elif co.etape== self:
356 # Cas 2 : le concept est produit par la macro (self)
357 # On est deja passe par type_sdprod (Cas 1 ou 3).
358 if co.etape==co._etape:
359 #Le concept a été créé par la macro (self)
360 #On peut changer son type
363 #Le concept a été créé par une macro parente
364 # Le type du concept doit etre coherent avec le type demande (seulement derive)
365 if not isinstance(co,t):
366 raise AsException("""Erreur interne.
367 Le type demande (%s) et le type du concept (%s) devraient etre derives""" %(t,co.__class__))
369 self.sdprods.append(co)
371 elif co.etape== self.parent:
372 # Cas 3 : le concept est produit par la macro parente (self.parent)
373 # on transfere la propriete du concept a la macro fille
374 # et on change le type du concept comme demande
375 # Au prealable, on verifie que le concept existant (co) est une instance
376 # possible du type demande (t)
377 # Cette règle est normalement cohérente avec les règles de vérification des mots-clés
378 if not isinstance(co,t):
379 raise AsException("""
380 Impossible de changer le type du concept produit (%s) en (%s).
381 Le type actuel (%s) devrait etre une classe derivee du nouveau type (%s)""" % (co,t,co.__class__,t))
382 mcs=self.get_mcs_with_co(co)
384 raise AsException("""Erreur interne.
385 Il ne devrait y avoir qu'un seul mot cle porteur du concept CO (%s)""" % co)
387 if not self.typeCO in mcs.definition.type:
388 raise AsException("""Erreur interne.
389 Impossible de changer le type du concept (%s). Le mot cle associe ne supporte pas CO mais seulement (%s)""" %(co,mcs.definition.type))
391 # On ne change pas le type car il respecte la condition isinstance(co,t)
393 self.sdprods.append(co)
395 elif self.issubstep(co.etape):
396 # Cas 4 : Le concept est propriété d'une sous etape de la macro (self).
397 # On est deja passe par type_sdprod (Cas 3 ou 1).
398 # Il suffit de le mettre dans la liste des concepts produits (self.sdprods)
399 # Le type du concept et t doivent etre derives.
400 # Il n'y a aucune raison pour que la condition ne soit pas verifiee.
401 if not isinstance(co,t):
402 raise AsException("""Erreur interne.
403 Le type demande (%s) et le type du concept (%s) devraient etre derives""" %(t,co.__class__))
404 self.sdprods.append(co)
407 # Cas 5 : le concept est produit par une autre étape
411 def issubstep(self,etape):
413 Cette methode retourne un entier indiquant si etape est une
414 sous etape de la macro self ou non
418 if etape in self.etapes:return 1
419 for etap in self.etapes:
420 if etap.issubstep(etape):return 1
423 def register(self,etape):
425 Enregistrement de etape dans le contexte de la macro : liste etapes
426 et demande d enregistrement global aupres du JDC
428 self.etapes.append(etape)
429 idetape=self.jdc.g_register(etape)
434 Methode appelee dans l __init__ d un ASSD a sa creation pour
435 s enregistrer (reserve aux ASSD créés au sein d'une MACRO)
438 return self.jdc.o_register(sd)
440 def create_sdprod(self,etape,nomsd):
442 Cette methode doit fabriquer le concept produit retourne
443 par l'etape etape et le nommer.
445 Elle est appelée à l'initiative de l'etape
446 pendant le processus de construction de cette etape : methode __call__
447 de la classe CMD (OPER ou MACRO)
448 Ce travail est réalisé par le contexte supérieur (etape.parent)
449 car dans certains cas, le concept ne doit pas etre fabriqué mais
450 l'etape doit simplement utiliser un concept préexistant.
451 - Cas 1 : etape.reuse != None : le concept est réutilisé
452 - Cas 2 : l'étape appartient à une macro qui a déclaré un concept
453 de sortie qui doit etre produit par cette etape.
455 if self.Outputs.has_key(nomsd):
456 # Il s'agit d'un concept de sortie de la macro. Il ne faut pas le créer
457 # Il faut quand meme appeler la fonction sd_prod si elle existe.
458 # get_type_produit le fait et donne le type attendu par la commande pour verification ultérieure.
459 sdprod=etape.get_type_produit_brut()
460 sd=self.Outputs[nomsd]
461 # On verifie que le type du concept existant sd.__class__ est un sur type de celui attendu
462 # Cette règle est normalement cohérente avec les règles de vérification des mots-clés
463 if not issubclass(sdprod,sd.__class__):
464 raise AsException("Le type du concept produit %s devrait etre une sur classe de %s" %(sd.__class__,sdprod))
465 # La propriete du concept est transferee a l'etape avec le type attendu par l'étape
468 # On donne au concept le type produit par la sous commande.
469 # Le principe est le suivant : apres avoir verifie que le type deduit par la sous commande
470 # est bien coherent avec celui initialement affecte par la macro (voir ci dessus)
471 # on affecte au concept ce type car il peut etre plus precis (derive, en general)
473 # On force également le nom stocké dans l'attribut sdnom : on lui donne le nom
474 # du concept associé à nomsd
476 elif etape.definition.reentrant != 'n' and etape.reuse != None:
477 # On est dans le cas d'une commande avec reutilisation d'un concept existant
478 # get_sd_prod fait le necessaire : verifications, associations, etc. mais ne cree
479 # pas un nouveau concept. Il retourne le concept reutilise
480 sd= etape.get_sd_prod()
481 # Dans le cas d'un concept nomme automatiquement : _xxx, __xxx,
482 # On force le nom stocke dans l'attribut sdnom de l'objet etape : on lui donne le nom
483 # du concept reutilise (sd ou etape.reuse c'est pareil)
484 # Ceci est indispensable pour eviter des erreurs lors des verifications des macros
485 # En effet une commande avec reutilisation d'un concept verifie que le nom de
486 # la variable a gauche du signe = est le meme que celui du concept reutilise.
487 # Lorsqu'une telle commande apparait dans une macro, on supprime cette verification.
488 if (etape.sdnom == '' or etape.sdnom[0] == '_'):
491 # On est dans le cas de la creation d'un nouveau concept
492 sd= etape.get_sd_prod()
494 self.NommerSdprod(sd,nomsd)
497 def NommerSdprod(self,sd,sdnom,restrict='non'):
499 Cette methode est appelee par les etapes internes de la macro
500 La macro appelle le JDC pour valider le nommage
501 On considere que l espace de nom est unique et géré par le JDC
502 Si le nom est deja utilise, l appel leve une exception
503 Si restrict=='non', on insere le concept dans le contexte de la macro
504 Si restrict=='oui', on n'insere pas le concept dans le contexte de la macro
506 # Normalement, lorsqu'on appelle cette methode, on ne veut nommer que des concepts nouvellement crees.
507 # Le filtrage sur les concepts a creer ou a ne pas creer est fait dans la methode
508 # create_sdprod. La seule chose a verifier apres conversion eventuelle du nom
509 # est de verifier que le nom n'est pas deja attribue. Ceci est fait en delegant
510 # au JDC par l'intermediaire du parent.
512 #XXX attention inconsistence : prefix et gcncon ne sont pas
513 # définis dans le package Noyau. La methode NommerSdprod pour
514 # les macros devrait peut etre etre déplacée dans Build ???
516 if CONTEXT.debug : print "MACRO.NommerSdprod: ",sd,sdnom
518 if hasattr(self,'prefix'):
519 # Dans le cas de l'include_materiau on ajoute un prefixe au nom du concept
520 if sdnom != self.prefix:sdnom=self.prefix+sdnom
522 if self.Outputs.has_key(sdnom):
523 # Il s'agit d'un concept de sortie de la macro produit par une sous commande
524 sdnom=self.Outputs[sdnom].nom
525 elif sdnom != '' and sdnom[0] == '_':
526 # Si le nom du concept commence par le caractere _ on lui attribue
527 # un identificateur JEVEUX construit par gcncon et respectant
528 # la regle gcncon legerement adaptee ici
529 # nom commencant par __ : il s'agit de concepts qui seront detruits
530 # nom commencant par _ : il s'agit de concepts intermediaires qui seront gardes
531 # ATTENTION : il faut traiter différemment les concepts dont le nom
532 # commence par _ mais qui sont des concepts nommés automatiquement par
533 # une éventuelle sous macro.
534 # Le test suivant n'est pas tres rigoureux mais permet de fonctionner pour le moment (a améliorer)
535 if sdnom[1] in string.digits:
536 # Ce concept provient probablement d'une macro appelee par self
538 elif sdnom[1] == '_':
539 sdnom=self.gcncon('.')
541 sdnom=self.gcncon('_')
543 # On est dans le cas d'un nom de concept global.
546 if restrict == 'non':
547 # On demande le nommage au parent mais sans ajout du concept dans le contexte du parent
548 # car on va l'ajouter dans le contexte de la macro
549 self.parent.NommerSdprod(sd,sdnom,restrict='oui')
550 # On ajoute dans le contexte de la macro les concepts nommes
551 # Ceci est indispensable pour les CO (macro) dans un INCLUDE
552 self.g_context[sdnom]=sd
554 # La demande de nommage vient probablement d'une macro qui a mis
555 # le concept dans son contexte. On ne traite plus que le nommage (restrict="oui")
556 self.parent.NommerSdprod(sd,sdnom,restrict='oui')
558 def delete_concept_after_etape(self,etape,sd):
560 Met à jour les étapes de la MACRO qui sont après etape suite à
561 la disparition du concept sd
563 # Cette methode est définie dans le noyau mais ne sert que pendant la phase de creation
564 # des etapes et des concepts. Il n'y a aucun traitement particulier à réaliser
565 # Dans d'autres conditions, il faudrait surcharger cette méthode.
568 def accept(self,visitor):
570 Cette methode permet de parcourir l'arborescence des objets
571 en utilisant le pattern VISITEUR
573 visitor.visitMACRO_ETAPE(self)
575 def update_context(self,d):
577 Met à jour le contexte contenu dans le dictionnaire d
578 Une MACRO_ETAPE peut ajouter plusieurs concepts dans le contexte
579 Une fonction enregistree dans op_init peut egalement modifier le contexte
581 if type(self.definition.op_init) == types.FunctionType:
582 apply(self.definition.op_init,(self,d))
583 if self.sd != None:d[self.sd.nom]=self.sd
584 for co in self.sdprods:
587 def make_include(self,unite=None):
589 Inclut un fichier dont l'unite logique est unite
591 if not unite : return
592 f,text=self.get_file(unite=unite,fic_origine=self.parent.nom)
593 self.fichier_init = f
595 self.make_contexte(f,text)
597 def make_poursuite(self):
599 Inclut un fichier poursuite
602 f,text=self.get_file(fic_origine=self.parent.nom)
604 raise AsException("Impossible d'ouvrir la base pour une poursuite")
607 self.make_contexte(f,text)
609 def make_contexte(self,f,text):
611 Interprete le texte fourni (text) issu du fichier f
612 dans le contexte du parent.
613 Cette methode est utile pour le fonctionnement des
616 # on execute le texte fourni dans le contexte forme par
617 # le contexte de l etape pere (global au sens Python)
618 # et le contexte de l etape (local au sens Python)
619 code=compile(text,f,'exec')
622 self.contexte_fichier_init = d
623 globs=self.parent.get_global_contexte()
626 def get_global_contexte(self):
628 Cette methode retourne le contexte global fourni
629 par le parent(self) a une etape fille (l'appelant) pour
630 realiser des evaluations de texte Python (INCLUDE,...)
632 # Le contexte global est forme par concatenation du contexte
633 # du parent de self et de celui de l'etape elle meme (self)
634 d=self.parent.get_global_contexte()
635 d.update(self.g_context)
639 """ Méthode qui retourne une copie de self non enregistrée auprès du JDC
641 On surcharge la methode de ETAPE pour exprimer que les concepts crees
642 par la MACRO d'origine ne sont pas crees par la copie mais eventuellement
645 etape=N_ETAPE.ETAPE.copy(self)
649 def copy_intern(self,etape):
650 """ Cette méthode effectue la recopie des etapes internes d'une macro
651 passée en argument (etape)
654 for etp in etape.etapes:
656 new_etp.copy_reuse(etp)
657 new_etp.copy_sdnom(etp)
658 new_etp.reparent(self)
660 new_sd = etp.sd.__class__(etape=new_etp)
663 new_sd.set_name(etp.sd.nom)
665 self.NommerSdprod(new_sd,etp.sd.nom)
666 new_etp.copy_intern(etp)
667 self.etapes.append(new_etp)
669 def reset_jdc(self,new_jdc):
671 Reinitialise l'etape avec un nouveau jdc parent new_jdc
673 if self.sd and self.reuse == None :
674 self.parent.NommerSdprod(self.sd,self.sd.nom)
675 for concept in self.sdprods:
676 self.parent.NommerSdprod(concept,concept.nom)
678 def reparent(self,parent):
680 Cette methode sert a reinitialiser la parente de l'objet
682 N_ETAPE.ETAPE.reparent(self,parent)
683 #on ne change pas la parenté des concepts. On s'assure uniquement que le jdc en référence est le bon
684 for concept in self.sdprods:
686 for e in self.etapes: