1 #@ MODIF N_MACRO_ETAPE Noyau DATE 10/05/2006 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
41 class MACRO_ETAPE(N_ETAPE.ETAPE):
47 def __init__(self,oper=None,reuse=None,args={}):
51 - definition : objet portant les attributs de définition d'une étape
52 de type macro-commande. Il est initialisé par
55 - reuse : indique le concept d'entrée réutilisé. Il se trouvera donc
56 en sortie si les conditions d'exécution de l'opérateur
59 - valeur : arguments d'entrée de type mot-clé=valeur. Initialisé
67 self.parent=CONTEXT.get_current_step()
70 self.idracine=oper.label
71 self.appel=N_utils.callee_where()
75 self.current_context={}
76 self.index_etape_courante=0
79 # Dans le cas d'une macro écrite en Python, l'attribut Outputs est un
80 # dictionnaire qui contient les concepts produits de sortie
81 # (nom : ASSD) déclarés dans la fonction sd_prod
87 self.UserError="UserError"
89 def make_register(self):
91 Initialise les attributs jdc, id, niveau et réalise les enregistrements
95 self.jdc = self.parent.get_jdc_root()
96 self.id=self.parent.register(self)
98 self.UserError=self.jdc.UserError
100 self.jdc = self.parent =None
103 self.UserError="UserError"
105 def Build_sd(self,nom):
107 Construit le concept produit de l'opérateur. Deux cas
108 peuvent se présenter :
110 - le parent n'est pas défini. Dans ce cas, l'étape prend en charge
111 la création et le nommage du concept.
113 - le parent est défini. Dans ce cas, l'étape demande au parent la
114 création et le nommage du concept.
119 # On positionne la macro self en tant que current_step pour que les
120 # étapes créées lors de l'appel à sd_prod et à op_init aient la macro
122 self.set_current_step()
124 sd= self.parent.create_sdprod(self,nom)
125 if type(self.definition.op_init) == types.FunctionType:
126 apply(self.definition.op_init,(self,self.parent.g_context))
128 sd=self.get_sd_prod()
129 if sd != None and self.reuse == None:
130 # On ne nomme le concept que dans le cas de non reutilisation
133 self.reset_current_step()
134 except AsException,e:
135 self.reset_current_step()
136 raise AsException("Etape ",self.nom,'ligne : ',self.appel[0],
137 'fichier : ',self.appel[1],e)
138 except (EOFError,self.UserError):
139 # Le retablissement du step courant n'est pas strictement necessaire. On le fait pour des raisons de coherence
140 self.reset_current_step()
143 self.reset_current_step()
144 l=traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2])
145 raise AsException("Etape ",self.nom,'ligne : ',self.appel[0],
146 'fichier : ',self.appel[1]+'\n',
154 Marquage des concepts CO d'une macro-commande
156 # On marque les concepts CO pour verification ulterieure de leur bonne utilisation
159 #if not hasattr(c,"_etape") or c._etape is not c.etape:
163 def get_sd_prod(self):
165 Retourne le concept résultat d'une macro étape
166 La difference avec une etape ou une proc-etape tient a ce que
167 le concept produit peut exister ou pas
168 Si sd_prod == None le concept produit n existe pas on retourne None
170 cas 1 : sd_prod n'est pas une fonction
171 il s'agit d'une sous classe de ASSD
172 on construit le sd à partir de cette classe
174 cas 2 : sd_prod est une fonction
175 on l'évalue avec les mots-clés de l'étape (mc_liste)
176 on construit le sd à partir de la classe obtenue
179 sd_prod=self.definition.sd_prod
181 # On marque les concepts CO pour verification ulterieure de leur bonne utilisation
184 if type(self.definition.sd_prod) == types.FunctionType:
185 d=self.cree_dict_valeurs(self.mc_liste)
187 # la sd_prod d'une macro a l'objet macro_etape lui meme en premier argument
188 # Comme sd_prod peut invoquer la méthode type_sdprod qui ajoute
189 # les concepts produits dans self.sdprods, il faut le mettre à zéro avant de l'appeler
191 sd_prod= apply(sd_prod,(self,),d)
192 except (EOFError,self.UserError):
195 if CONTEXT.debug: traceback.print_exc()
196 l=traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2])
197 raise AsException("impossible d affecter un type au resultat\n",string.join(l[2:]))
199 # on teste maintenant si la SD est réutilisée ou s'il faut la créer
200 if self.definition.reentrant != 'n' and self.reuse:
201 # Le concept produit est specifie reutilise (reuse=xxx). C'est une erreur mais non fatale.
202 # Elle sera traitee ulterieurement.
208 self.sd= sd_prod(etape=self)
210 # Si la commande est obligatoirement reentrante et reuse n'a pas ete specifie, c'est une erreur.
211 # On ne fait rien ici. L'erreur sera traitee par la suite.
214 def get_type_produit(self,force=0):
216 return self.get_type_produit_brut(force)
218 #traceback.print_exc()
221 def get_type_produit_brut(self,force=0):
223 Retourne le type du concept résultat de l'étape et eventuellement type
224 les concepts produits "à droite" du signe égal (en entrée)
226 cas 1 : sd_prod de oper n'est pas une fonction
227 il s'agit d'une sous classe de ASSD
228 on retourne le nom de la classe
229 cas 2 : il s'agit d'une fonction
230 on l'évalue avec les mots-clés de l'étape (mc_liste)
231 et on retourne son résultat
233 if not force and hasattr(self,'typret'): return self.typret
234 # On marque les concepts CO pour verification ulterieure de leur bonne utilisation
237 if type(self.definition.sd_prod) == types.FunctionType:
238 d=self.cree_dict_valeurs(self.mc_liste)
239 # Comme sd_prod peut invoquer la méthode type_sdprod qui ajoute
240 # les concepts produits dans self.sdprods, il faut le mettre à zéro
242 sd_prod= apply(self.definition.sd_prod,(self,),d)
244 sd_prod=self.definition.sd_prod
247 def get_contexte_avant(self,etape):
249 Retourne le dictionnaire des concepts connus avant etape
250 pour les commandes internes a la macro
251 On tient compte des commandes qui modifient le contexte
252 comme DETRUIRE ou les macros
254 # L'étape courante pour laquelle le contexte a été calculé est
255 # mémorisée dans self.index_etape_courante
256 # Si on insère des commandes (par ex, dans EFICAS), il faut
257 # préalablement remettre ce pointeur à 0
259 index_etape=self.etapes.index(etape)
261 index_etape=len(self.etapes)
263 if index_etape >= self.index_etape_courante:
264 # On calcule le contexte en partant du contexte existant
265 d=self.current_context
266 liste_etapes=self.etapes[self.index_etape_courante:index_etape]
268 d=self.current_context={}
269 liste_etapes=self.etapes
271 for e in liste_etapes:
276 self.index_etape_courante=index_etape
281 Méthode qui supprime toutes les références arrières afin que
282 l'objet puisse etre correctement détruit par le garbage collector
284 N_MCCOMPO.MCCOMPO.supprime(self)
287 if self.sd : self.sd.supprime()
288 for concept in self.sdprods:
290 for etape in self.etapes:
293 def type_sdprod(self,co,t):
295 Cette methode a pour fonction de typer le concept co avec le type t
296 dans les conditions suivantes
297 1- co est un concept produit de self
298 2- co est un concept libre : on le type et on l attribue à self
299 Elle enregistre egalement les concepts produits (on fait l hypothese
300 que la liste sdprods a été correctement initialisee, vide probablement)
302 if not hasattr(co,'etape'):
303 # Le concept vaut None probablement. On ignore l'appel
306 # On cherche a discriminer les differents cas de typage d'un concept
307 # produit par une macro qui est specifie dans un mot cle simple.
308 # On peut passer plusieurs fois par type_sdprod ce qui explique
309 # le nombre important de cas.
311 # Cas 1 : Le concept est libre. Il vient d'etre cree par CO(nom)
312 # Cas 2 : Le concept est produit par la macro. On est deja passe par type_sdprod.
313 # Cas semblable a Cas 1.
314 # Cas 3 : Le concept est produit par la macro englobante (parent). On transfere
315 # la propriete du concept de la macro parent a la macro courante (self)
316 # en verifiant que le type est valide
317 # Cas 4 : La concept est la propriete d'une etape fille. Ceci veut dire qu'on est
318 # deja passe par type_sdprod et que la propriete a ete transfere a une
319 # etape fille. Cas semblable a Cas 3.
320 # Cas 5 : Le concept est produit par une etape externe a la macro.
323 # Cas 1 : le concept est libre
324 # On l'attache a la macro et on change son type dans le type demande
325 # Recherche du mot cle simple associe au concept
326 mcs=self.get_mcs_with_co(co)
328 raise AsException("""Erreur interne.
329 Il ne devrait y avoir qu'un seul mot cle porteur du concept CO (%s)""" % co)
331 if not self.typeCO in mcs.definition.type:
332 raise AsException("""Erreur interne.
333 Impossible de changer le type du concept (%s). Le mot cle associe ne supporte pas CO mais seulement (%s)""" %(co,mcs.definition.type))
336 self.sdprods.append(co)
338 elif co.etape== self:
339 # Cas 2 : le concept est produit par la macro (self)
340 # On est deja passe par type_sdprod (Cas 1 ou 3).
341 # Il suffit de le mettre dans la liste des concepts produits (self.sdprods)
342 # Le type du concept doit etre coherent avec le type demande (seulement derive)
343 if not isinstance(co,t):
344 raise AsException("""Erreur interne.
345 Le type demande (%s) et le type du concept (%s) devraient etre derives""" %(t,co.__class__))
346 self.sdprods.append(co)
348 elif co.etape== self.parent:
349 # Cas 3 : le concept est produit par la macro parente (self.parent)
350 # on transfere la propriete du concept a la macro fille
351 # et on change le type du concept comme demande
352 # Au prealable, on verifie que le concept existant (co) est une instance
353 # possible du type demande (t)
354 # Cette règle est normalement cohérente avec les règles de vérification des mots-clés
355 if not isinstance(co,t):
356 raise AsException("""
357 Impossible de changer le type du concept produit (%s) en (%s).
358 Le type actuel (%s) devrait etre une classe derivee du nouveau type (%s)""" % (co,t,co.__class__,t))
359 mcs=self.get_mcs_with_co(co)
361 raise AsException("""Erreur interne.
362 Il ne devrait y avoir qu'un seul mot cle porteur du concept CO (%s)""" % co)
364 if not self.typeCO in mcs.definition.type:
365 raise AsException("""Erreur interne.
366 Impossible de changer le type du concept (%s). Le mot cle associe ne supporte pas CO mais seulement (%s)""" %(co,mcs.definition.type))
368 # On ne change pas le type car il respecte la condition isinstance(co,t)
370 self.sdprods.append(co)
372 elif self.issubstep(co.etape):
373 # Cas 4 : Le concept est propriété d'une sous etape de la macro (self).
374 # On est deja passe par type_sdprod (Cas 3 ou 1).
375 # Il suffit de le mettre dans la liste des concepts produits (self.sdprods)
376 # Le type du concept et t doivent etre derives.
377 # Il n'y a aucune raison pour que la condition ne soit pas verifiee.
378 if not isinstance(co,t):
379 raise AsException("""Erreur interne.
380 Le type demande (%s) et le type du concept (%s) devraient etre derives""" %(t,co.__class__))
381 self.sdprods.append(co)
384 # Cas 5 : le concept est produit par une autre étape
388 def issubstep(self,etape):
390 Cette methode retourne un entier indiquant si etape est une
391 sous etape de la macro self ou non
395 if etape in self.etapes:return 1
396 for etap in self.etapes:
397 if etap.issubstep(etape):return 1
400 def register(self,etape):
402 Enregistrement de etape dans le contexte de la macro : liste etapes
403 et demande d enregistrement global aupres du JDC
405 self.etapes.append(etape)
406 idetape=self.jdc.g_register(etape)
411 Methode appelee dans l __init__ d un ASSD a sa creation pour
412 s enregistrer (reserve aux ASSD créés au sein d'une MACRO)
415 return self.jdc.o_register(sd)
417 def create_sdprod(self,etape,nomsd):
419 Intention : Cette methode doit fabriquer le concept produit retourne
420 par l'etape etape et le nommer.
421 Elle est appelée à l'initiative de l'etape
422 pendant le processus de construction de cette etape : methode __call__
423 de la classe CMD (OPER ou MACRO)
424 Ce travail est réalisé par le contexte supérieur (etape.parent)
425 car dans certains cas, le concept ne doit pas etre fabriqué mais
426 l'etape doit simplement utiliser un concept préexistant.
427 Cas 1 : etape.reuse != None : le concept est réutilisé
428 Cas 2 : l'étape appartient à une macro qui a déclaré un concept
429 de sortie qui doit etre produit par cette etape.
431 if self.Outputs.has_key(nomsd):
432 # Il s'agit d'un concept de sortie de la macro. Il ne faut pas le créer
433 # Il faut quand meme appeler la fonction sd_prod si elle existe.
434 # get_type_produit le fait et donne le type attendu par la commande pour verification ultérieure.
435 sdprod=etape.get_type_produit_brut()
436 sd=self.Outputs[nomsd]
437 # On verifie que le type du concept existant sd.__class__ est un sur type de celui attendu
438 # Cette règle est normalement cohérente avec les règles de vérification des mots-clés
439 if not issubclass(sdprod,sd.__class__):
440 raise AsException("Le type du concept produit %s devrait etre une sur classe de %s" %(sd.__class__,sdprod))
441 # La propriete du concept est transferee a l'etape avec le type attendu par l'étape
444 # On donne au concept le type produit par la sous commande.
445 # Le principe est le suivant : apres avoir verifie que le type deduit par la sous commande
446 # est bien coherent avec celui initialement affecte par la macro (voir ci dessus)
447 # on affecte au concept ce type car il peut etre plus precis (derive, en general)
449 # On force également le nom stocké dans l'attribut sdnom : on lui donne le nom
450 # du concept associé à nomsd
452 elif etape.definition.reentrant != 'n' and etape.reuse != None:
453 # On est dans le cas d'une commande avec reutilisation d'un concept existant
454 # get_sd_prod fait le necessaire : verifications, associations, etc. mais ne cree
455 # pas un nouveau concept. Il retourne le concept reutilise
456 sd= etape.get_sd_prod()
457 # Dans le cas d'un concept nomme automatiquement : _xxx, __xxx,
458 # On force le nom stocke dans l'attribut sdnom de l'objet etape : on lui donne le nom
459 # du concept reutilise (sd ou etape.reuse c'est pareil)
460 # Ceci est indispensable pour eviter des erreurs lors des verifications des macros
461 # En effet une commande avec reutilisation d'un concept verifie que le nom de
462 # la variable a gauche du signe = est le meme que celui du concept reutilise.
463 # Lorsqu'une telle commande apparait dans une macro, on supprime cette verification.
464 if (etape.sdnom == '' or etape.sdnom[0] == '_'):
467 # On est dans le cas de la creation d'un nouveau concept
468 sd= etape.get_sd_prod()
470 self.NommerSdprod(sd,nomsd)
473 def NommerSdprod(self,sd,sdnom,restrict='non'):
475 Cette methode est appelee par les etapes internes de la macro
476 La macro appelle le JDC pour valider le nommage
477 On considere que l espace de nom est unique et géré par le JDC
478 Si le nom est deja utilise, l appel leve une exception
479 Si restrict=='non', on insere le concept dans le contexte de la macro
480 Si restrict=='oui', on n'insere pas le concept dans le contexte de la macro
482 # Normalement, lorsqu'on appelle cette methode, on ne veut nommer que des concepts nouvellement crees.
483 # Le filtrage sur les concepts a creer ou a ne pas creer est fait dans la methode
484 # create_sdprod. La seule chose a verifier apres conversion eventuelle du nom
485 # est de verifier que le nom n'est pas deja attribue. Ceci est fait en delegant
486 # au JDC par l'intermediaire du parent.
488 #XXX attention inconsistence : prefix et gcncon ne sont pas
489 # définis dans le package Noyau. La methode NommerSdprod pour
490 # les macros devrait peut etre etre déplacée dans Build ???
492 if CONTEXT.debug : print "MACRO.NommerSdprod: ",sd,sdnom
494 if hasattr(self,'prefix'):
495 # Dans le cas de l'include_materiau on ajoute un prefixe au nom du concept
496 if sdnom != self.prefix:sdnom=self.prefix+sdnom
498 if self.Outputs.has_key(sdnom):
499 # Il s'agit d'un concept de sortie de la macro produit par une sous commande
500 sdnom=self.Outputs[sdnom].nom
501 elif sdnom != '' and sdnom[0] == '_':
502 # Si le nom du concept commence par le caractere _ on lui attribue
503 # un identificateur JEVEUX construit par gcncon et respectant
504 # la regle gcncon legerement adaptee ici
505 # nom commencant par __ : il s'agit de concepts qui seront detruits
506 # nom commencant par _ : il s'agit de concepts intermediaires qui seront gardes
507 # ATTENTION : il faut traiter différemment les concepts dont le nom
508 # commence par _ mais qui sont des concepts nommés automatiquement par
509 # une éventuelle sous macro.
510 # Le test suivant n'est pas tres rigoureux mais permet de fonctionner pour le moment (a améliorer)
511 if sdnom[1] in string.digits:
512 # Ce concept provient probablement d'une macro appelee par self
514 elif sdnom[1] == '_':
515 sdnom=self.gcncon('.')
517 sdnom=self.gcncon('_')
519 # On est dans le cas d'un nom de concept global.
522 if restrict == 'non':
523 # On demande le nommage au parent mais sans ajout du concept dans le contexte du parent
524 # car on va l'ajouter dans le contexte de la macro
525 self.parent.NommerSdprod(sd,sdnom,restrict='oui')
526 # On ajoute dans le contexte de la macro les concepts nommes
527 # Ceci est indispensable pour les CO (macro) dans un INCLUDE
528 self.g_context[sdnom]=sd
530 # La demande de nommage vient probablement d'une macro qui a mis
531 # le concept dans son contexte. On ne traite plus que le nommage (restrict="oui")
532 self.parent.NommerSdprod(sd,sdnom,restrict='oui')
534 def delete_concept_after_etape(self,etape,sd):
536 Met à jour les étapes de la MACRO qui sont après etape suite à
537 la disparition du concept sd
539 # Cette methode est définie dans le noyau mais ne sert que pendant la phase de creation
540 # des etapes et des concepts. Il n'y a aucun traitement particulier à réaliser
541 # Dans d'autres conditions, il faudrait surcharger cette méthode.
544 def accept(self,visitor):
546 Cette methode permet de parcourir l'arborescence des objets
547 en utilisant le pattern VISITEUR
549 visitor.visitMACRO_ETAPE(self)
551 def update_context(self,d):
553 Met à jour le contexte contenu dans le dictionnaire d
554 Une MACRO_ETAPE peut ajouter plusieurs concepts dans le contexte
555 Une fonction enregistree dans op_init peut egalement modifier le contexte
557 if type(self.definition.op_init) == types.FunctionType:
558 apply(self.definition.op_init,(self,d))
559 if self.sd != None:d[self.sd.nom]=self.sd
560 for co in self.sdprods:
563 def make_include(self,unite=None):
565 Inclut un fichier dont l'unite logique est unite
567 if not unite : return
568 f,text=self.get_file(unite=unite,fic_origine=self.parent.nom)
569 self.fichier_init = f
571 self.make_contexte(f,text)
573 def make_poursuite(self):
575 Inclut un fichier poursuite
578 f,text=self.get_file(fic_origine=self.parent.nom)
580 raise AsException("Impossible d'ouvrir la base pour une poursuite")
583 self.make_contexte(f,text)
585 def make_contexte(self,f,text):
587 Interprete le texte fourni (text) issu du fichier f
588 dans le contexte du parent.
589 Cette methode est utile pour le fonctionnement des
592 # on execute le texte fourni dans le contexte forme par
593 # le contexte de l etape pere (global au sens Python)
594 # et le contexte de l etape (local au sens Python)
595 code=compile(text,f,'exec')
598 self.contexte_fichier_init = d
599 globs=self.parent.get_global_contexte()
602 def get_global_contexte(self):
604 Cette methode retourne le contexte global fourni
605 par le parent(self) a une etape fille (l'appelant) pour
606 realiser des evaluations de texte Python (INCLUDE,...)
608 # Le contexte global est forme par concatenation du contexte
609 # du parent de self et de celui de l'etape elle meme (self)
610 d=self.parent.get_global_contexte()
611 d.update(self.g_context)
615 """ Méthode qui retourne une copie de self non enregistrée auprès du JDC
617 On surcharge la methode de ETAPE pour exprimer que les concepts crees
618 par la MACRO d'origine ne sont pas crees par la copie mais eventuellement
621 etape=N_ETAPE.ETAPE.copy(self)
625 def copy_intern(self,etape):
626 """ Cette méthode effectue la recopie des etapes internes d'une macro
627 passée en argument (etape)
630 for etp in etape.etapes:
632 new_etp.copy_reuse(etp)
633 new_etp.copy_sdnom(etp)
634 new_etp.reparent(self)
636 new_sd = etp.sd.__class__(etape=new_etp)
639 new_sd.nom = etp.sd.nom
641 self.NommerSdprod(new_sd,etp.sd.nom)
642 new_etp.copy_intern(etp)
643 self.etapes.append(new_etp)
645 def reset_jdc(self,new_jdc):
647 Reinitialise l'etape avec un nouveau jdc parent new_jdc
649 if self.sd and self.reuse == None :
650 self.parent.NommerSdprod(self.sd,self.sd.nom)
651 for concept in self.sdprods:
652 self.parent.NommerSdprod(concept,concept.nom)
654 def reparent(self,parent):
656 Cette methode sert a reinitialiser la parente de l'objet
658 N_ETAPE.ETAPE.reparent(self,parent)
659 #on ne change pas la parenté des concepts. On s'assure uniquement que le jdc en référence est le bon
660 for concept in self.sdprods:
662 for e in self.etapes: