1 #@ MODIF N_MACRO_ETAPE Noyau DATE 22/02/2005 AUTEUR DURAND C.DURAND
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):
46 def __init__(self,oper=None,reuse=None,args={}):
50 - definition : objet portant les attributs de définition d'une étape
51 de type macro-commande. Il est initialisé par
54 - reuse : indique le concept d'entrée réutilisé. Il se trouvera donc
55 en sortie si les conditions d'exécution de l'opérateur
58 - valeur : arguments d'entrée de type mot-clé=valeur. Initialisé
66 self.parent=CONTEXT.get_current_step()
69 self.idracine=oper.label
70 self.appel=N_utils.callee_where()
74 self.current_context={}
75 self.index_etape_courante=0
78 # Dans le cas d'une macro écrite en Python, l'attribut Outputs est un
79 # dictionnaire qui contient les concepts produits de sortie
80 # (nom : ASSD) déclarés dans la fonction sd_prod
87 def make_register(self):
89 Initialise les attributs jdc, id, niveau et réalise les enregistrements
93 self.jdc = self.parent.get_jdc_root()
94 self.id=self.parent.register(self)
96 self.UserError=self.jdc.UserError
98 self.jdc = self.parent =None
101 self.UserError="UserError"
103 def Build_sd(self,nom):
105 Construit le concept produit de l'opérateur. Deux cas
106 peuvent se présenter :
108 - le parent n'est pas défini. Dans ce cas, l'étape prend en charge
109 la création et le nommage du concept.
111 - le parent est défini. Dans ce cas, l'étape demande au parent la
112 création et le nommage du concept.
115 if not self.isactif():return
118 # On positionne la macro self en tant que current_step pour que les
119 # étapes créées lors de l'appel à sd_prod et à op_init aient la macro
121 self.set_current_step()
123 sd= self.parent.create_sdprod(self,nom)
124 if type(self.definition.op_init) == types.FunctionType:
125 apply(self.definition.op_init,(self,self.parent.g_context))
127 sd=self.get_sd_prod()
128 if sd != None and self.reuse == None:
129 # On ne nomme le concept que dans le cas de non reutilisation
132 self.reset_current_step()
133 except AsException,e:
134 self.reset_current_step()
135 raise AsException("Etape ",self.nom,'ligne : ',self.appel[0],
136 'fichier : ',self.appel[1],e)
137 except (EOFError,self.UserError):
138 # Le retablissement du step courant n'est pas strictement necessaire. On le fait pour des raisons de coherence
139 self.reset_current_step()
142 self.reset_current_step()
143 l=traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2])
144 raise AsException("Etape ",self.nom,'ligne : ',self.appel[0],
145 'fichier : ',self.appel[1]+'\n',
153 Marquage des concepts CO d'une macro-commande
155 # On marque les concepts CO pour verification ulterieure de leur bonne utilisation
158 #if not hasattr(c,"_etape") or c._etape is not c.etape:
162 def get_sd_prod(self):
164 Retourne le concept résultat d'une macro étape
165 La difference avec une etape ou une proc-etape tient a ce que
166 le concept produit peut exister ou pas
167 Si sd_prod == None le concept produit n existe pas on retourne None
169 cas 1 : sd_prod n'est pas une fonction
170 il s'agit d'une sous classe de ASSD
171 on construit le sd à partir de cette classe
173 cas 2 : sd_prod est une fonction
174 on l'évalue avec les mots-clés de l'étape (mc_liste)
175 on construit le sd à partir de la classe obtenue
178 sd_prod=self.definition.sd_prod
180 # On marque les concepts CO pour verification ulterieure de leur bonne utilisation
183 if type(self.definition.sd_prod) == types.FunctionType:
184 d=self.cree_dict_valeurs(self.mc_liste)
186 # la sd_prod d'une macro a l'objet macro_etape lui meme en premier argument
187 # Comme sd_prod peut invoquer la méthode type_sdprod qui ajoute
188 # les concepts produits dans self.sdprods, il faut le mettre à zéro avant de l'appeler
190 sd_prod= apply(sd_prod,(self,),d)
191 except (EOFError,self.UserError):
194 if CONTEXT.debug: traceback.print_exc()
195 l=traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2])
196 raise AsException("impossible d affecter un type au resultat\n",string.join(l[2:]))
198 # on teste maintenant si la SD est réutilisée ou s'il faut la créer
199 if self.definition.reentrant != 'n' and self.reuse:
200 # Le concept produit est specifie reutilise (reuse=xxx). C'est une erreur mais non fatale.
201 # Elle sera traitee ulterieurement.
207 self.sd= sd_prod(etape=self)
209 # Si la commande est obligatoirement reentrante et reuse n'a pas ete specifie, c'est une erreur.
210 # On ne fait rien ici. L'erreur sera traitee par la suite.
213 def get_type_produit(self,force=0):
215 return self.get_type_produit_brut(force)
217 #traceback.print_exc()
220 def get_type_produit_brut(self,force=0):
222 Retourne le type du concept résultat de l'étape et eventuellement type
223 les concepts produits "à droite" du signe égal (en entrée)
225 cas 1 : sd_prod de oper n'est pas une fonction
226 il s'agit d'une sous classe de ASSD
227 on retourne le nom de la classe
228 cas 2 : il s'agit d'une fonction
229 on l'évalue avec les mots-clés de l'étape (mc_liste)
230 et on retourne son résultat
232 if not force and hasattr(self,'typret'): return self.typret
233 # On marque les concepts CO pour verification ulterieure de leur bonne utilisation
236 if type(self.definition.sd_prod) == types.FunctionType:
237 d=self.cree_dict_valeurs(self.mc_liste)
238 # Comme sd_prod peut invoquer la méthode type_sdprod qui ajoute
239 # les concepts produits dans self.sdprods, il faut le mettre à zéro
241 sd_prod= apply(self.definition.sd_prod,(self,),d)
243 sd_prod=self.definition.sd_prod
246 def get_contexte_avant(self,etape):
248 Retourne le dictionnaire des concepts connus avant etape
249 pour les commandes internes a la macro
250 On tient compte des commandes qui modifient le contexte
251 comme DETRUIRE ou les macros
253 # L'étape courante pour laquelle le contexte a été calculé est
254 # mémorisée dans self.index_etape_courante
255 # Si on insère des commandes (par ex, dans EFICAS), il faut
256 # préalablement remettre ce pointeur à 0
258 index_etape=self.etapes.index(etape)
260 index_etape=len(self.etapes)
262 if index_etape >= self.index_etape_courante:
263 # On calcule le contexte en partant du contexte existant
264 d=self.current_context
265 liste_etapes=self.etapes[self.index_etape_courante:index_etape]
267 d=self.current_context={}
268 liste_etapes=self.etapes
270 for e in liste_etapes:
275 self.index_etape_courante=index_etape
280 Méthode qui supprime toutes les références arrières afin que
281 l'objet puisse etre correctement détruit par le garbage collector
283 N_MCCOMPO.MCCOMPO.supprime(self)
286 if self.sd : self.sd.supprime()
287 for concept in self.sdprods:
289 for etape in self.etapes:
292 def type_sdprod(self,co,t):
294 Cette methode a pour fonction de typer le concept co avec le type t
295 dans les conditions suivantes
296 1- co est un concept produit de self
297 2- co est un concept libre : on le type et on l attribue à self
298 Elle enregistre egalement les concepts produits (on fait l hypothese
299 que la liste sdprods a été correctement initialisee, vide probablement)
301 if not hasattr(co,'etape'):
302 # Le concept vaut None probablement. On ignore l'appel
305 # On cherche a discriminer les differents cas de typage d'un concept
306 # produit par une macro qui est specifie dans un mot cle simple.
307 # On peut passer plusieurs fois par type_sdprod ce qui explique
308 # le nombre important de cas.
310 # Cas 1 : Le concept est libre. Il vient d'etre cree par CO(nom)
311 # Cas 2 : Le concept est produit par la macro. On est deja passe par type_sdprod.
312 # Cas semblable a Cas 1.
313 # Cas 3 : Le concept est produit par la macro englobante (parent). On transfere
314 # la propriete du concept de la macro parent a la macro courante (self)
315 # en verifiant que le type est valide
316 # Cas 4 : La concept est la propriete d'une etape fille. Ceci veut dire qu'on est
317 # deja passe par type_sdprod et que la propriete a ete transfere a une
318 # etape fille. Cas semblable a Cas 3.
319 # Cas 5 : Le concept est produit par une etape externe a la macro.
322 # Cas 1 : le concept est libre
323 # On l'attache a la macro et on change son type dans le type demande
324 # Recherche du mot cle simple associe au concept
325 mcs=self.get_mcs_with_co(co)
327 raise AsException("""Erreur interne.
328 Il ne devrait y avoir qu'un seul mot cle porteur du concept CO (%s)""" % co)
330 if not CO in mcs.definition.type:
331 raise AsException("""Erreur interne.
332 Impossible de changer le type du concept (%s). Le mot cle associe ne supporte pas CO mais seulement (%s)""" %(co,mcs.definition.type))
335 self.sdprods.append(co)
337 elif co.etape== self:
338 # Cas 2 : le concept est produit par la macro (self)
339 # On est deja passe par type_sdprod (Cas 1 ou 3).
340 # Il suffit de le mettre dans la liste des concepts produits (self.sdprods)
341 # Le type du concept doit etre coherent avec le type demande (seulement derive)
342 if not isinstance(co,t):
343 raise AsException("""Erreur interne.
344 Le type demande (%s) et le type du concept (%s) devraient etre derives""" %(t,co.__class__))
345 self.sdprods.append(co)
347 elif co.etape== self.parent:
348 # Cas 3 : le concept est produit par la macro parente (self.parent)
349 # on transfere la propriete du concept a la macro fille
350 # et on change le type du concept comme demande
351 # Au prealable, on verifie que le concept existant (co) est une instance
352 # possible du type demande (t)
353 # Cette règle est normalement cohérente avec les règles de vérification des mots-clés
354 if not isinstance(co,t):
355 raise AsException("""
356 Impossible de changer le type du concept produit (%s) en (%s).
357 Le type actuel (%s) devrait etre une classe derivee du nouveau type (%s)""" % (co,t,co.__class__,t))
358 mcs=self.get_mcs_with_co(co)
360 raise AsException("""Erreur interne.
361 Il ne devrait y avoir qu'un seul mot cle porteur du concept CO (%s)""" % co)
363 if not CO in mcs.definition.type:
364 raise AsException("""Erreur interne.
365 Impossible de changer le type du concept (%s). Le mot cle associe ne supporte pas CO mais seulement (%s)""" %(co,mcs.definition.type))
367 # On ne change pas le type car il respecte la condition isinstance(co,t)
369 self.sdprods.append(co)
371 elif self.issubstep(co.etape):
372 # Cas 4 : Le concept est propriété d'une sous etape de la macro (self).
373 # On est deja passe par type_sdprod (Cas 3 ou 1).
374 # Il suffit de le mettre dans la liste des concepts produits (self.sdprods)
375 # Le type du concept et t doivent etre derives.
376 # Il n'y a aucune raison pour que la condition ne soit pas verifiee.
377 if not isinstance(co,t):
378 raise AsException("""Erreur interne.
379 Le type demande (%s) et le type du concept (%s) devraient etre derives""" %(t,co.__class__))
380 self.sdprods.append(co)
383 # Cas 5 : le concept est produit par une autre étape
387 def issubstep(self,etape):
389 Cette methode retourne un entier indiquant si etape est une
390 sous etape de la macro self ou non
394 if etape in self.etapes:return 1
395 for etap in self.etapes:
396 if etap.issubstep(etape):return 1
399 def register(self,etape):
401 Enregistrement de etape dans le contexte de la macro : liste etapes
402 et demande d enregistrement global aupres du JDC
404 self.etapes.append(etape)
405 idetape=self.jdc.g_register(etape)
410 Methode appelee dans l __init__ d un ASSD a sa creation pour
411 s enregistrer (reserve aux ASSD créés au sein d'une MACRO)
414 return self.jdc.o_register(sd)
416 def create_sdprod(self,etape,nomsd):
418 Intention : Cette methode doit fabriquer le concept produit retourne
419 par l'etape etape et le nommer.
420 Elle est appelée à l'initiative de l'etape
421 pendant le processus de construction de cette etape : methode __call__
422 de la classe CMD (OPER ou MACRO)
423 Ce travail est réalisé par le contexte supérieur (etape.parent)
424 car dans certains cas, le concept ne doit pas etre fabriqué mais
425 l'etape doit simplement utiliser un concept préexistant.
426 Cas 1 : etape.reuse != None : le concept est réutilisé
427 Cas 2 : l'étape appartient à une macro qui a déclaré un concept
428 de sortie qui doit etre produit par cette etape.
430 if self.Outputs.has_key(nomsd):
431 # Il s'agit d'un concept de sortie de la macro. Il ne faut pas le créer
432 # Il faut quand meme appeler la fonction sd_prod si elle existe.
433 # get_type_produit le fait et donne le type attendu par la commande pour verification ultérieure.
434 sdprod=etape.get_type_produit_brut()
435 sd=self.Outputs[nomsd]
436 # On verifie que le type du concept existant sd.__class__ est un sur type de celui attendu
437 # Cette règle est normalement cohérente avec les règles de vérification des mots-clés
438 if not issubclass(sdprod,sd.__class__):
439 raise AsException("Le type du concept produit %s devrait etre une sur classe de %s" %(sd.__class__,sdprod))
440 # La propriete du concept est transferee a l'etape avec le type attendu par l'étape
443 # On donne au concept le type produit par la sous commande.
444 # Le principe est le suivant : apres avoir verifie que le type deduit par la sous commande
445 # est bien coherent avec celui initialement affecte par la macro (voir ci dessus)
446 # on affecte au concept ce type car il peut etre plus precis (derive, en general)
448 # On force également le nom stocké dans l'attribut sdnom : on lui donne le nom
449 # du concept associé à nomsd
451 elif etape.definition.reentrant != 'n' and etape.reuse != None:
452 # On est dans le cas d'une commande avec reutilisation d'un concept existant
453 # get_sd_prod fait le necessaire : verifications, associations, etc. mais ne cree
454 # pas un nouveau concept. Il retourne le concept reutilise
455 sd= etape.get_sd_prod()
456 # Dans le cas d'un concept nomme automatiquement : _xxx, __xxx,
457 # On force le nom stocke dans l'attribut sdnom de l'objet etape : on lui donne le nom
458 # du concept reutilise (sd ou etape.reuse c'est pareil)
459 # Ceci est indispensable pour eviter des erreurs lors des verifications des macros
460 # En effet une commande avec reutilisation d'un concept verifie que le nom de
461 # la variable a gauche du signe = est le meme que celui du concept reutilise.
462 # Lorsqu'une telle commande apparait dans une macro, on supprime cette verification.
463 if (etape.sdnom == '' or etape.sdnom[0] == '_'):
466 # On est dans le cas de la creation d'un nouveau concept
467 sd= etape.get_sd_prod()
469 self.NommerSdprod(sd,nomsd)
472 def NommerSdprod(self,sd,sdnom,restrict='non'):
474 Cette methode est appelee par les etapes internes de la macro
475 La macro appelle le JDC pour valider le nommage
476 On considere que l espace de nom est unique et géré par le JDC
477 Si le nom est deja utilise, l appel leve une exception
478 Si restrict=='non', on insere le concept dans le contexte de la macro
479 Si restrict=='oui', on n'insere pas le concept dans le contexte de la macro
481 # Normalement, lorsqu'on appelle cette methode, on ne veut nommer que des concepts nouvellement crees.
482 # Le filtrage sur les concepts a creer ou a ne pas creer est fait dans la methode
483 # create_sdprod. La seule chose a verifier apres conversion eventuelle du nom
484 # est de verifier que le nom n'est pas deja attribue. Ceci est fait en delegant
485 # au JDC par l'intermediaire du parent.
487 #XXX attention inconsistence : prefix et gcncon ne sont pas
488 # définis dans le package Noyau. La methode NommerSdprod pour
489 # les macros devrait peut etre etre déplacée dans Build ???
491 if CONTEXT.debug : print "MACRO.NommerSdprod: ",sd,sdnom
493 if hasattr(self,'prefix'):
494 # Dans le cas de l'include_materiau on ajoute un prefixe au nom du concept
495 if sdnom != self.prefix:sdnom=self.prefix+sdnom
497 if self.Outputs.has_key(sdnom):
498 # Il s'agit d'un concept de sortie de la macro produit par une sous commande
499 sdnom=self.Outputs[sdnom].nom
500 elif sdnom != '' and sdnom[0] == '_':
501 # Si le nom du concept commence par le caractere _ on lui attribue
502 # un identificateur JEVEUX construit par gcncon et respectant
503 # la regle gcncon legerement adaptee ici
504 # nom commencant par __ : il s'agit de concepts qui seront detruits
505 # nom commencant par _ : il s'agit de concepts intermediaires qui seront gardes
506 # ATTENTION : il faut traiter différemment les concepts dont le nom
507 # commence par _ mais qui sont des concepts nommés automatiquement par
508 # une éventuelle sous macro.
509 # Le test suivant n'est pas tres rigoureux mais permet de fonctionner pour le moment (a améliorer)
510 if sdnom[1] in string.digits:
511 # Ce concept provient probablement d'une macro appelee par self
513 elif sdnom[1] == '_':
514 sdnom=self.gcncon('.')
516 sdnom=self.gcncon('_')
518 # On est dans le cas d'un nom de concept global.
521 if restrict == 'non':
522 # On demande le nommage au parent mais sans ajout du concept dans le contexte du parent
523 # car on va l'ajouter dans le contexte de la macro
524 self.parent.NommerSdprod(sd,sdnom,restrict='oui')
525 # On ajoute dans le contexte de la macro les concepts nommes
526 # Ceci est indispensable pour les CO (macro) dans un INCLUDE
527 self.g_context[sdnom]=sd
529 # La demande de nommage vient probablement d'une macro qui a mis
530 # le concept dans son contexte. On ne traite plus que le nommage (restrict="oui")
531 self.parent.NommerSdprod(sd,sdnom,restrict='oui')
533 def delete_concept_after_etape(self,etape,sd):
535 Met à jour les étapes de la MACRO qui sont après etape suite à
536 la disparition du concept sd
538 # Cette methode est définie dans le noyau mais ne sert que pendant la phase de creation
539 # des etapes et des concepts. Il n'y a aucun traitement particulier à réaliser
540 # Dans d'autres conditions, il faudrait surcharger cette méthode.
543 def accept(self,visitor):
545 Cette methode permet de parcourir l'arborescence des objets
546 en utilisant le pattern VISITEUR
548 visitor.visitMACRO_ETAPE(self)
550 def update_context(self,d):
552 Met à jour le contexte contenu dans le dictionnaire d
553 Une MACRO_ETAPE peut ajouter plusieurs concepts dans le contexte
554 Une fonction enregistree dans op_init peut egalement modifier le contexte
556 if type(self.definition.op_init) == types.FunctionType:
557 apply(self.definition.op_init,(self,d))
558 if self.sd != None:d[self.sd.nom]=self.sd
559 for co in self.sdprods:
562 def make_include(self,unite=None):
564 Inclut un fichier dont l'unite logique est unite
566 if not unite : return
567 f,text=self.get_file(unite=unite,fic_origine=self.parent.nom)
568 self.fichier_init = f
570 self.make_contexte(f,text)
572 def make_poursuite(self):
574 Inclut un fichier poursuite
577 f,text=self.get_file(fic_origine=self.parent.nom)
579 raise AsException("Impossible d'ouvrir la base pour une poursuite")
582 self.make_contexte(f,text)
584 def make_contexte(self,f,text):
586 Interprete le texte fourni (text) issu du fichier f
587 dans le contexte du parent.
588 Cette methode est utile pour le fonctionnement des
591 # on execute le texte fourni dans le contexte forme par
592 # le contexte de l etape pere (global au sens Python)
593 # et le contexte de l etape (local au sens Python)
594 code=compile(text,f,'exec')
597 self.contexte_fichier_init = d
598 globs=self.parent.get_global_contexte()
601 def get_global_contexte(self):
603 Cette methode retourne le contexte global fourni
604 par le parent(self) a une etape fille (l'appelant) pour
605 realiser des evaluations de texte Python (INCLUDE,...)
607 # Le contexte global est forme par concatenation du contexte
608 # du parent de self et de celui de l'etape elle meme (self)
609 d=self.parent.get_global_contexte()
610 d.update(self.g_context)
614 """ Méthode qui retourne une copie de self non enregistrée auprès du JDC
616 On surcharge la methode de ETAPE pour exprimer que les concepts crees
617 par la MACRO d'origine ne sont pas crees par la copie mais eventuellement
620 etape=N_ETAPE.ETAPE.copy(self)
624 def copy_intern(self,etape):
625 """ Cette méthode effectue la recopie des etapes internes d'une macro
626 passée en argument (etape)
629 for etp in etape.etapes:
631 new_etp.copy_reuse(etp)
632 new_etp.copy_sdnom(etp)
633 new_etp.reparent(self)
635 new_sd = etp.sd.__class__(etape=new_etp)
638 new_sd.nom = etp.sd.nom
640 self.NommerSdprod(new_sd,etp.sd.nom)
641 new_etp.copy_intern(etp)
642 self.etapes.append(new_etp)