1 #@ MODIF N_MACRO_ETAPE Noyau DATE 28/11/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
81 # Dans le cas d'une macro écrite en Python, l'attribut Outputs est un
82 # dictionnaire qui contient les concepts produits de sortie
83 # (nom : ASSD) déclarés dans la fonction sd_prod
89 self.UserError="UserError"
91 def make_register(self):
93 Initialise les attributs jdc, id, niveau et réalise les enregistrements
97 self.jdc = self.parent.get_jdc_root()
98 self.id=self.parent.register(self)
100 self.UserError=self.jdc.UserError
102 self.jdc = self.parent =None
105 self.UserError="UserError"
107 def Build_sd(self,nom):
109 Construit le concept produit de l'opérateur. Deux cas
110 peuvent se présenter :
112 - le parent n'est pas défini. Dans ce cas, l'étape prend en charge
113 la création et le nommage du concept.
115 - le parent est défini. Dans ce cas, l'étape demande au parent la
116 création et le nommage du concept.
121 # On positionne la macro self en tant que current_step pour que les
122 # étapes créées lors de l'appel à sd_prod et à op_init aient la macro
124 self.set_current_step()
126 sd= self.parent.create_sdprod(self,nom)
127 if type(self.definition.op_init) == types.FunctionType:
128 apply(self.definition.op_init,(self,self.parent.g_context))
130 sd=self.get_sd_prod()
131 if sd != None and self.reuse == None:
132 # On ne nomme le concept que dans le cas de non reutilisation
135 self.reset_current_step()
136 except AsException,e:
137 self.reset_current_step()
138 raise AsException("Etape ",self.nom,'ligne : ',self.appel[0],
139 'fichier : ',self.appel[1],e)
140 except (EOFError,self.UserError):
141 # Le retablissement du step courant n'est pas strictement necessaire. On le fait pour des raisons de coherence
142 self.reset_current_step()
145 self.reset_current_step()
146 l=traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2])
147 raise AsException("Etape ",self.nom,'ligne : ',self.appel[0],
148 'fichier : ',self.appel[1]+'\n',
156 Marquage des concepts CO d'une macro-commande
158 # On marque les concepts CO pour verification ulterieure de leur bonne utilisation
161 #if not hasattr(c,"_etape") or c._etape is not c.etape:
165 def get_sd_prod(self):
167 Retourne le concept résultat d'une macro étape
168 La difference avec une etape ou une proc-etape tient a ce que
169 le concept produit peut exister ou pas
171 Si sd_prod == None le concept produit n existe pas on retourne None
174 - cas 1 : sd_prod n'est pas une fonction
175 il s'agit d'une sous classe de ASSD
176 on construit le sd à partir de cette classe
178 - cas 2 : sd_prod est une fonction
179 on l'évalue avec les mots-clés de l'étape (mc_liste)
180 on construit le sd à partir de la classe obtenue
183 sd_prod=self.definition.sd_prod
185 # On marque les concepts CO pour verification ulterieure de leur bonne utilisation
188 if type(self.definition.sd_prod) == types.FunctionType:
189 d=self.cree_dict_valeurs(self.mc_liste)
191 # la sd_prod d'une macro a l'objet macro_etape lui meme en premier argument
192 # Comme sd_prod peut invoquer la méthode type_sdprod qui ajoute
193 # les concepts produits dans self.sdprods, il faut le mettre à zéro avant de l'appeler
195 sd_prod= apply(sd_prod,(self,),d)
196 except (EOFError,self.UserError):
199 if CONTEXT.debug: traceback.print_exc()
200 l=traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2])
201 raise AsException("impossible d affecter un type au resultat\n",string.join(l[2:]))
203 # on teste maintenant si la SD est réutilisée ou s'il faut la créer
204 if self.definition.reentrant != 'n' and self.reuse:
205 # Le concept produit est specifie reutilise (reuse=xxx). C'est une erreur mais non fatale.
206 # Elle sera traitee ulterieurement.
212 self.sd= sd_prod(etape=self)
214 # Si la commande est obligatoirement reentrante et reuse n'a pas ete specifie, c'est une erreur.
215 # On ne fait rien ici. L'erreur sera traitee par la suite.
217 if self.sd is not None and not isinstance(self.sd, ASSD):
218 raise AsException("""
219 Impossible de typer le résultat !
221 Utilisateur : Soit la valeur fournie derrière "reuse" est incorrecte,
222 soit il y a une "," à la fin d'une commande précédente.
223 Développeur : La fonction "sd_prod" retourne un type invalide.""")
226 def get_type_produit(self,force=0):
228 return self.get_type_produit_brut(force)
230 #traceback.print_exc()
233 def get_type_produit_brut(self,force=0):
235 Retourne le type du concept résultat de l'étape et eventuellement type
236 les concepts produits "à droite" du signe égal (en entrée)
239 - cas 1 : sd_prod de oper n'est pas une fonction
240 il s'agit d'une sous classe de ASSD
241 on retourne le nom de la classe
242 - cas 2 : il s'agit d'une fonction
243 on l'évalue avec les mots-clés de l'étape (mc_liste)
244 et on retourne son résultat
246 if not force and hasattr(self,'typret'): return self.typret
247 # On marque les concepts CO pour verification ulterieure de leur bonne utilisation
250 if type(self.definition.sd_prod) == types.FunctionType:
251 d=self.cree_dict_valeurs(self.mc_liste)
252 # Comme sd_prod peut invoquer la méthode type_sdprod qui ajoute
253 # les concepts produits dans self.sdprods, il faut le mettre à zéro
255 sd_prod= apply(self.definition.sd_prod,(self,),d)
257 sd_prod=self.definition.sd_prod
260 def get_contexte_avant(self,etape):
262 Retourne le dictionnaire des concepts connus avant etape
263 pour les commandes internes a la macro
264 On tient compte des commandes qui modifient le contexte
265 comme DETRUIRE ou les macros
267 # L'étape courante pour laquelle le contexte a été calculé est
268 # mémorisée dans self.index_etape_courante
269 # Si on insère des commandes (par ex, dans EFICAS), il faut
270 # préalablement remettre ce pointeur à 0
272 index_etape = self.index_etapes[etape]
274 index_etape=len(self.etapes)
276 if index_etape >= self.index_etape_courante:
277 # On calcule le contexte en partant du contexte existant
278 d=self.current_context
279 liste_etapes=self.etapes[self.index_etape_courante:index_etape]
281 d=self.current_context={}
282 liste_etapes=self.etapes
284 for e in liste_etapes:
289 self.index_etape_courante=index_etape
294 Méthode qui supprime toutes les références arrières afin que
295 l'objet puisse etre correctement détruit par le garbage collector
297 N_MCCOMPO.MCCOMPO.supprime(self)
300 if self.sd : self.sd.supprime()
301 for concept in self.sdprods:
303 for etape in self.etapes:
306 def type_sdprod(self,co,t):
308 Cette methode a pour fonction de typer le concept co avec le type t
309 dans les conditions suivantes :
310 1. co est un concept produit de self
311 2. co est un concept libre : on le type et on l attribue à self
313 Elle enregistre egalement les concepts produits (on fait l hypothese
314 que la liste sdprods a été correctement initialisee, vide probablement)
316 if not hasattr(co,'etape'):
317 # Le concept vaut None probablement. On ignore l'appel
320 # On cherche a discriminer les differents cas de typage d'un concept
321 # produit par une macro qui est specifie dans un mot cle simple.
322 # On peut passer plusieurs fois par type_sdprod ce qui explique
323 # le nombre important de cas.
325 # Cas 1 : Le concept est libre. Il vient d'etre cree par CO(nom)
326 # Cas 2 : Le concept est produit par la macro. On est deja passe par type_sdprod.
327 # Cas semblable a Cas 1.
328 # Cas 3 : Le concept est produit par la macro englobante (parent). On transfere
329 # la propriete du concept de la macro parent a la macro courante (self)
330 # en verifiant que le type est valide
331 # Cas 4 : La concept est la propriete d'une etape fille. Ceci veut dire qu'on est
332 # deja passe par type_sdprod et que la propriete a ete transfere a une
333 # etape fille. Cas semblable a Cas 3.
334 # Cas 5 : Le concept est produit par une etape externe a la macro.
337 # Cas 1 : le concept est libre
338 # On l'attache a la macro et on change son type dans le type demande
339 # Recherche du mot cle simple associe au concept
340 mcs=self.get_mcs_with_co(co)
342 raise AsException("""Erreur interne.
343 Il ne devrait y avoir qu'un seul mot cle porteur du concept CO (%s)""" % co)
345 if not self.typeCO in mcs.definition.type:
346 raise AsException("""Erreur interne.
347 Impossible de changer le type du concept (%s). Le mot cle associe ne supporte pas CO mais seulement (%s)""" %(co,mcs.definition.type))
349 # affectation du bon type du concept et
350 # initialisation de sa partie "sd"
351 if CONTEXT.debug:print "changement de type:",co,t
354 self.sdprods.append(co)
356 elif co.etape== self:
357 # Cas 2 : le concept est produit par la macro (self)
358 # On est deja passe par type_sdprod (Cas 1 ou 3).
359 if co.etape==co._etape:
360 #Le concept a été créé par la macro (self)
361 #On peut changer son type
364 #Le concept a été créé par une macro parente
365 # Le type du concept doit etre coherent avec le type demande (seulement derive)
366 if not isinstance(co,t):
367 raise AsException("""Erreur interne.
368 Le type demande (%s) et le type du concept (%s) devraient etre derives""" %(t,co.__class__))
370 self.sdprods.append(co)
372 elif co.etape== self.parent:
373 # Cas 3 : le concept est produit par la macro parente (self.parent)
374 # on transfere la propriete du concept a la macro fille
375 # et on change le type du concept comme demande
376 # Au prealable, on verifie que le concept existant (co) est une instance
377 # possible du type demande (t)
378 # Cette règle est normalement cohérente avec les règles de vérification des mots-clés
379 if not isinstance(co,t):
380 raise AsException("""
381 Impossible de changer le type du concept produit (%s) en (%s).
382 Le type actuel (%s) devrait etre une classe derivee du nouveau type (%s)""" % (co,t,co.__class__,t))
383 mcs=self.get_mcs_with_co(co)
385 raise AsException("""Erreur interne.
386 Il ne devrait y avoir qu'un seul mot cle porteur du concept CO (%s)""" % co)
388 if not self.typeCO in mcs.definition.type:
389 raise AsException("""Erreur interne.
390 Impossible de changer le type du concept (%s). Le mot cle associe ne supporte pas CO mais seulement (%s)""" %(co,mcs.definition.type))
392 # On ne change pas le type car il respecte la condition isinstance(co,t)
394 self.sdprods.append(co)
396 elif self.issubstep(co.etape):
397 # Cas 4 : Le concept est propriété d'une sous etape de la macro (self).
398 # On est deja passe par type_sdprod (Cas 3 ou 1).
399 # Il suffit de le mettre dans la liste des concepts produits (self.sdprods)
400 # Le type du concept et t doivent etre derives.
401 # Il n'y a aucune raison pour que la condition ne soit pas verifiee.
402 if not isinstance(co,t):
403 raise AsException("""Erreur interne.
404 Le type demande (%s) et le type du concept (%s) devraient etre derives""" %(t,co.__class__))
405 self.sdprods.append(co)
408 # Cas 5 : le concept est produit par une autre étape
412 def issubstep(self,etape):
414 Cette methode retourne un entier indiquant si etape est une
415 sous etape de la macro self ou non
419 if etape in self.etapes:return 1
420 for etap in self.etapes:
421 if etap.issubstep(etape):return 1
424 def register(self,etape):
426 Enregistrement de etape dans le contexte de la macro : liste etapes
427 et demande d enregistrement global aupres du JDC
429 self.etapes.append(etape)
430 self.index_etapes[etape] = len(self.etapes) - 1
431 idetape=self.jdc.g_register(etape)
436 Methode appelee dans l __init__ d un ASSD a sa creation pour
437 s enregistrer (reserve aux ASSD créés au sein d'une MACRO)
440 return self.jdc.o_register(sd)
442 def create_sdprod(self,etape,nomsd):
444 Cette methode doit fabriquer le concept produit retourne
445 par l'etape etape et le nommer.
447 Elle est appelée à l'initiative de l'etape
448 pendant le processus de construction de cette etape : methode __call__
449 de la classe CMD (OPER ou MACRO)
450 Ce travail est réalisé par le contexte supérieur (etape.parent)
451 car dans certains cas, le concept ne doit pas etre fabriqué mais
452 l'etape doit simplement utiliser un concept préexistant.
453 - Cas 1 : etape.reuse != None : le concept est réutilisé
454 - Cas 2 : l'étape appartient à une macro qui a déclaré un concept
455 de sortie qui doit etre produit par cette etape.
457 if self.Outputs.has_key(nomsd):
458 # Il s'agit d'un concept de sortie de la macro. Il ne faut pas le créer
459 # Il faut quand meme appeler la fonction sd_prod si elle existe.
460 # get_type_produit le fait et donne le type attendu par la commande pour verification ultérieure.
461 sdprod=etape.get_type_produit_brut()
462 sd=self.Outputs[nomsd]
463 # On verifie que le type du concept existant sd.__class__ est un sur type de celui attendu
464 # Cette règle est normalement cohérente avec les règles de vérification des mots-clés
465 if not issubclass(sdprod,sd.__class__):
466 raise AsException("Le type du concept produit %s devrait etre une sur classe de %s" %(sd.__class__,sdprod))
467 # La propriete du concept est transferee a l'etape avec le type attendu par l'étape
470 # On donne au concept le type produit par la sous commande.
471 # Le principe est le suivant : apres avoir verifie que le type deduit par la sous commande
472 # est bien coherent avec celui initialement affecte par la macro (voir ci dessus)
473 # on affecte au concept ce type car il peut etre plus precis (derive, en general)
475 # On force également le nom stocké dans l'attribut sdnom : on lui donne le nom
476 # du concept associé à nomsd
478 elif etape.definition.reentrant != 'n' and etape.reuse != None:
479 # On est dans le cas d'une commande avec reutilisation d'un concept existant
480 # get_sd_prod fait le necessaire : verifications, associations, etc. mais ne cree
481 # pas un nouveau concept. Il retourne le concept reutilise
482 sd= etape.get_sd_prod()
483 # Dans le cas d'un concept nomme automatiquement : _xxx, __xxx,
484 # On force le nom stocke dans l'attribut sdnom de l'objet etape : on lui donne le nom
485 # du concept reutilise (sd ou etape.reuse c'est pareil)
486 # Ceci est indispensable pour eviter des erreurs lors des verifications des macros
487 # En effet une commande avec reutilisation d'un concept verifie que le nom de
488 # la variable a gauche du signe = est le meme que celui du concept reutilise.
489 # Lorsqu'une telle commande apparait dans une macro, on supprime cette verification.
490 if (etape.sdnom == '' or etape.sdnom[0] == '_'):
493 # On est dans le cas de la creation d'un nouveau concept
494 sd= etape.get_sd_prod()
496 self.NommerSdprod(sd,nomsd)
499 def NommerSdprod(self,sd,sdnom,restrict='non'):
501 Cette methode est appelee par les etapes internes de la macro
502 La macro appelle le JDC pour valider le nommage
503 On considere que l espace de nom est unique et géré par le JDC
504 Si le nom est deja utilise, l appel leve une exception
505 Si restrict=='non', on insere le concept dans le contexte de la macro
506 Si restrict=='oui', on n'insere pas le concept dans le contexte de la macro
508 # Normalement, lorsqu'on appelle cette methode, on ne veut nommer que des concepts nouvellement crees.
509 # Le filtrage sur les concepts a creer ou a ne pas creer est fait dans la methode
510 # create_sdprod. La seule chose a verifier apres conversion eventuelle du nom
511 # est de verifier que le nom n'est pas deja attribue. Ceci est fait en delegant
512 # au JDC par l'intermediaire du parent.
514 #XXX attention inconsistence : prefix et gcncon ne sont pas
515 # définis dans le package Noyau. La methode NommerSdprod pour
516 # les macros devrait peut etre etre déplacée dans Build ???
518 if CONTEXT.debug : print "MACRO.NommerSdprod: ",sd,sdnom
520 if hasattr(self,'prefix'):
521 # Dans le cas de l'include_materiau on ajoute un prefixe au nom du concept
522 if sdnom != self.prefix:sdnom=self.prefix+sdnom
524 if self.Outputs.has_key(sdnom):
525 # Il s'agit d'un concept de sortie de la macro produit par une sous commande
526 sdnom=self.Outputs[sdnom].nom
527 elif sdnom != '' and sdnom[0] == '_':
528 # Si le nom du concept commence par le caractere _ on lui attribue
529 # un identificateur JEVEUX construit par gcncon et respectant
530 # la regle gcncon legerement adaptee ici
531 # nom commencant par __ : il s'agit de concepts qui seront detruits
532 # nom commencant par _ : il s'agit de concepts intermediaires qui seront gardes
533 # ATTENTION : il faut traiter différemment les concepts dont le nom
534 # commence par _ mais qui sont des concepts nommés automatiquement par
535 # une éventuelle sous macro.
536 # Le test suivant n'est pas tres rigoureux mais permet de fonctionner pour le moment (a améliorer)
537 if sdnom[1] in string.digits:
538 # Ce concept provient probablement d'une macro appelee par self
540 elif sdnom[1] == '_':
541 sdnom=self.gcncon('.')
543 sdnom=self.gcncon('_')
545 # On est dans le cas d'un nom de concept global.
548 if restrict == 'non':
549 # On demande le nommage au parent mais sans ajout du concept dans le contexte du parent
550 # car on va l'ajouter dans le contexte de la macro
551 self.parent.NommerSdprod(sd,sdnom,restrict='oui')
552 # On ajoute dans le contexte de la macro les concepts nommes
553 # Ceci est indispensable pour les CO (macro) dans un INCLUDE
554 self.g_context[sdnom]=sd
556 # La demande de nommage vient probablement d'une macro qui a mis
557 # le concept dans son contexte. On ne traite plus que le nommage (restrict="oui")
558 self.parent.NommerSdprod(sd,sdnom,restrict='oui')
560 def delete_concept_after_etape(self,etape,sd):
562 Met à jour les étapes de la MACRO qui sont après etape suite à
563 la disparition du concept sd
565 # Cette methode est définie dans le noyau mais ne sert que pendant la phase de creation
566 # des etapes et des concepts. Il n'y a aucun traitement particulier à réaliser
567 # Dans d'autres conditions, il faudrait surcharger cette méthode.
570 def accept(self,visitor):
572 Cette methode permet de parcourir l'arborescence des objets
573 en utilisant le pattern VISITEUR
575 visitor.visitMACRO_ETAPE(self)
577 def update_context(self,d):
579 Met à jour le contexte contenu dans le dictionnaire d
580 Une MACRO_ETAPE peut ajouter plusieurs concepts dans le contexte
581 Une fonction enregistree dans op_init peut egalement modifier le contexte
583 if type(self.definition.op_init) == types.FunctionType:
584 apply(self.definition.op_init,(self,d))
585 if self.sd != None:d[self.sd.nom]=self.sd
586 for co in self.sdprods:
589 def make_include(self,unite=None):
591 Inclut un fichier dont l'unite logique est unite
593 if not unite : return
594 f,text=self.get_file(unite=unite,fic_origine=self.parent.nom)
595 self.fichier_init = f
597 self.make_contexte(f,text)
599 def make_poursuite(self):
601 Inclut un fichier poursuite
604 f,text=self.get_file(fic_origine=self.parent.nom)
606 raise AsException("Impossible d'ouvrir la base pour une poursuite")
609 self.make_contexte(f,text)
611 def make_contexte(self,f,text):
613 Interprete le texte fourni (text) issu du fichier f
614 dans le contexte du parent.
615 Cette methode est utile pour le fonctionnement des
618 # on execute le texte fourni dans le contexte forme par
619 # le contexte de l etape pere (global au sens Python)
620 # et le contexte de l etape (local au sens Python)
621 code=compile(text,f,'exec')
624 self.contexte_fichier_init = d
625 globs=self.parent.get_global_contexte()
628 def get_global_contexte(self):
630 Cette methode retourne le contexte global fourni
631 par le parent(self) a une etape fille (l'appelant) pour
632 realiser des evaluations de texte Python (INCLUDE,...)
634 # Le contexte global est forme par concatenation du contexte
635 # du parent de self et de celui de l'etape elle meme (self)
636 d=self.parent.get_global_contexte()
637 d.update(self.g_context)
641 """ Méthode qui retourne une copie de self non enregistrée auprès du JDC
643 On surcharge la methode de ETAPE pour exprimer que les concepts crees
644 par la MACRO d'origine ne sont pas crees par la copie mais eventuellement
647 etape=N_ETAPE.ETAPE.copy(self)
651 def copy_intern(self,etape):
652 """ Cette méthode effectue la recopie des etapes internes d'une macro
653 passée en argument (etape)
657 for etp in etape.etapes:
659 new_etp.copy_reuse(etp)
660 new_etp.copy_sdnom(etp)
661 new_etp.reparent(self)
663 new_sd = etp.sd.__class__(etape=new_etp)
666 new_sd.set_name(etp.sd.nom)
668 self.NommerSdprod(new_sd,etp.sd.nom)
669 new_etp.copy_intern(etp)
670 self.etapes.append(new_etp)
671 self.index_etapes[new_etp] = len(self.etapes) - 1
674 def reset_jdc(self,new_jdc):
676 Reinitialise l'etape avec un nouveau jdc parent new_jdc
678 if self.sd and self.reuse == None :
679 self.parent.NommerSdprod(self.sd,self.sd.nom)
680 for concept in self.sdprods:
681 self.parent.NommerSdprod(concept,concept.nom)
683 def reparent(self,parent):
685 Cette methode sert a reinitialiser la parente de l'objet
687 N_ETAPE.ETAPE.reparent(self,parent)
688 #on ne change pas la parenté des concepts. On s'assure uniquement que le jdc en référence est le bon
689 for concept in self.sdprods:
691 for e in self.etapes: