1 #@ MODIF N_JDC Noyau DATE 01/04/2008 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 JDC qui sert à interpréter un jeu de commandes
29 import os,string,traceback
30 import types,sys,linecache
35 from N_Exception import AsException
36 from N_ASSD import ASSD
41 MemoryErrorMsg = """MemoryError :
43 En général, cette erreur se produit car la mémoire utilisée hors du fortran
44 (jeveux) est importante.
47 - le calcul produit de gros objets Python dans une macro-commande ou
48 dans le jeu de commande lui-même,
49 - le calcul appelle un solveur (MUMPS par exemple) ou un outil externe
50 qui a besoin de mémoire hors jeveux,
51 - utilisation de jeveux dynamique,
55 - distinguer la mémoire limite du calcul (case "Mémoire totale" de astk)
56 de la mémoire réservée à jeveux (case "dont Aster"), le reste étant
57 disponible pour les allocations dynamiques.
62 class JDC(N_OBJECT.OBJECT):
64 Cette classe interprete un jeu de commandes fourni sous
65 la forme d'une chaine de caractères
69 Attributs d'instance :
81 from N_utils import SEP
83 def __init__(self,definition=None,procedure=None,cata=None,
84 cata_ord_dico=None,parent=None,
85 nom='SansNom',appli=None,context_ini=None,**args):
86 self.procedure=procedure
87 self.definition = definition
89 if type(self.cata) != types.TupleType and cata != None:
90 self.cata=(self.cata,)
91 self.cata_ordonne_dico=cata_ord_dico
95 self.context_ini=context_ini
96 # On conserve les arguments supplémentaires. Il est possible de passer
97 # des informations globales au JDC par ce moyen. Il pourrait etre plus
98 # sur de mettre en place le mecanisme des mots-cles pour verifier la
99 # validité des valeurs passées.
101 # On initialise avec les parametres de la definition puis on
102 # update avec ceux du JDC
103 self.args=self.definition.args
104 self.args.update(args)
109 self.regles=definition.regles
110 self.code = definition.code
115 # Creation de l objet compte rendu pour collecte des erreurs
117 self.cr = self.CR(debut = "CR phase d'initialisation",
118 fin = "fin CR phase d'initialisation")
119 # on met le jdc lui-meme dans le context global pour l'avoir sous
120 # l'etiquette "jdc" dans le fichier de commandes
121 self.g_context={ 'jdc' : self }
122 # Liste pour stocker tous les concepts produits dans le JDC
124 # Dictionnaire pour stocker tous les concepts du JDC (acces rapide par le nom)
127 self.index_etapes = {}
129 self.current_context={}
130 self.condition_context={}
131 self.index_etape_courante=0
132 self.UserError="UserError"
137 Cette methode compile la chaine procedure
138 Si des erreurs se produisent, elles sont consignées dans le
142 if self.appli != None :
143 self.appli.affiche_infos('Compilation du fichier de commandes en cours ...')
144 self.proc_compile=compile(self.procedure,self.nom,'exec')
145 except SyntaxError, e:
146 if CONTEXT.debug : traceback.print_exc()
147 l=traceback.format_exception_only(SyntaxError,e)
148 self.cr.exception("Compilation impossible : "+string.join(l))
149 except MemoryError, e:
150 self.cr.exception(MemoryErrorMsg)
151 except SystemError, e:
152 erreurs_connues = """
154 - offset too large : liste trop longue derrière un mot-clé.
155 Solution : liste = (valeurs, ..., )
158 l=traceback.format_exception_only(SystemError,e)
159 l.append(erreurs_connues)
160 self.cr.exception("Compilation impossible : " + ''.join(l))
163 def exec_compile(self):
165 Cette méthode execute le jeu de commandes compilé dans le contexte
166 self.g_context de l'objet JDC
168 CONTEXT.set_current_step(self)
169 # Le module nommage utilise le module linecache pour accéder
170 # au source des commandes du jeu de commandes.
171 # Dans le cas d'un fichier, on accède au contenu de ce fichier
172 # Dans le cas d'une chaine de caractères il faut accéder
173 # aux commandes qui sont dans la chaine
175 linecache.cache[self.nom]=0,0,string.split(self.procedure,'\n'),self.nom
177 exec self.exec_init in self.g_context
178 for obj_cata in self.cata:
179 if type(obj_cata) == types.ModuleType :
180 init2 = "from "+obj_cata.__name__+" import *"
181 exec init2 in self.g_context
183 # Initialisation du contexte global pour l'évaluation des conditions de BLOC
184 # On utilise une copie de l'initialisation du contexte du jdc
185 self.condition_context=self.g_context.copy()
187 # Si l'attribut context_ini n'est pas vide, on ajoute au contexte global
188 # le contexte initial (--> permet d'évaluer un JDC en récupérant un contexte
189 # d'un autre par exemple)
190 if self.context_ini :
191 self.g_context.update(self.context_ini)
192 # Update du dictionnaire des concepts
193 for sdnom,sd in self.context_ini.items():
194 if isinstance(sd,ASSD):self.sds_dict[sdnom]=sd
196 if self.appli != None :
197 self.appli.affiche_infos('Interprétation du fichier de commandes en cours ...')
198 # On sauve le contexte pour garder la memoire des constantes
199 # En mode edition (EFICAS) ou lors des verifications le contexte
201 # mais les constantes sont perdues
202 self.const_context=self.g_context
203 exec self.proc_compile in self.g_context
205 CONTEXT.unset_current_step()
206 if self.appli != None : self.appli.affiche_infos('')
209 # Exception utilise pour interrompre un jeu
210 # de commandes avant la fin
211 # Fonctionnement normal, ne doit pas etre considere comme une erreur
212 CONTEXT.unset_current_step()
213 self.affiche_fin_exec()
214 self.traiter_fin_exec('commande')
216 except AsException,e:
217 # une erreur a ete identifiee
219 traceback.print_exc()
220 # l'exception a été récupérée avant (où, comment ?),
221 # donc on cherche dans le texte
223 if txt.find('MemoryError') >= 0:
225 self.cr.exception(txt)
226 CONTEXT.unset_current_step()
229 etype, value, tb = sys.exc_info()
230 l= traceback.extract_tb(tb)
231 s= traceback.format_exception_only("Erreur de nom",e)[0][:-1]
232 message = "erreur de syntaxe, %s ligne %d" % (s,l[-1][1])
234 traceback.print_exc()
235 self.cr.exception(message)
236 CONTEXT.unset_current_step()
238 except self.UserError,exc_val:
239 self.traiter_user_exception(exc_val)
240 CONTEXT.unset_current_step()
241 self.affiche_fin_exec()
242 self.traiter_fin_exec('commande')
246 # sys_exc_typ,sys_exc_value,sys_exc_frame = sys_exc.info()
247 # (tuple de 3 éléments)
248 if CONTEXT.debug : traceback.print_exc()
250 exc_typ,exc_val,exc_fr=sys.exc_info()
251 l=traceback.format_exception(exc_typ,exc_val,exc_fr)
252 self.cr.exception("erreur non prevue et non traitee prevenir la maintenance "+
253 self.nom+'\n'+ string.join(l))
254 del exc_typ,exc_val,exc_fr
255 CONTEXT.unset_current_step()
257 def affiche_fin_exec(self):
259 Cette methode realise l'affichage final des statistiques de temps
260 apres l'execution de toutes
261 les commandes en mode commande par commande ou par lot
262 Elle doit etre surchargee pour en introduire un
266 def traiter_fin_exec(self,mode,etape=None):
268 Cette methode realise un traitement final apres l'execution de toutes
269 les commandes en mode commande par commande ou par lot
270 Par defaut il n'y a pas de traitement. Elle doit etre surchargee
271 pour en introduire un
273 print "FIN D'EXECUTION",mode,etape
275 def traiter_user_exception(self,exc_val):
276 """Cette methode realise un traitement sur les exceptions utilisateur
277 Par defaut il n'y a pas de traitement. La méthode doit etre
278 surchargée pour en introduire un.
282 def register(self,etape):
284 Cette méthode ajoute etape dans la liste des etapes : self.etapes
285 et retourne un numéro d'enregistrement
287 self.etapes.append(etape)
288 self.index_etapes[etape] = len(self.etapes) - 1
289 return self.g_register(etape)
291 def o_register(self,sd):
293 Retourne un identificateur pour concept
296 nom=sd.idracine + self.SEP + `self.nsd`
299 def g_register(self,etape):
301 Retourne un identificateur pour etape
303 self.nstep=self.nstep+1
304 idetape=etape.idracine + self.SEP + `self.nstep`
307 def create_sdprod(self,etape,nomsd):
309 Cette methode doit fabriquer le concept produit retourne
310 par l'etape etape et le nommer.
312 Elle est appelée à l'initiative de l'etape
313 pendant le processus de construction de cette etape :
314 methode __call__ de la classe CMD (OPER ou MACRO)
316 Ce travail est réalisé par le contexte supérieur
317 (etape.parent) car dans certains cas, le concept ne doit
318 pas etre fabriqué mais l'etape doit simplement utiliser
319 un concept préexistant.
322 - Cas 1 : etape.reuse != None : le concept est réutilisé
323 - Cas 2 : l'étape appartient à une macro qui a déclaré un
324 concept de sortie qui doit etre produit par cette
326 Dans le cas du JDC, le deuxième cas ne peut pas se produire.
328 sd= etape.get_sd_prod()
329 if sd != None and (etape.definition.reentrant == 'n' or etape.reuse is None) :
330 # ATTENTION : On ne nomme la SD que dans le cas de non reutilisation
331 # d un concept. Commande non reentrante ou reuse absent.
332 self.NommerSdprod(sd,nomsd)
335 def NommerSdprod(self,sd,sdnom,restrict='non'):
337 Nomme la SD apres avoir verifie que le nommage est possible : nom
339 Si le nom est deja utilise, leve une exception
340 Met le concept créé dans le concept global g_context
342 if CONTEXT.debug : print "JDC.NommerSdprod ",sd,sdnom
344 o=self.sds_dict.get(sdnom,None)
345 if isinstance(o,ASSD):
346 raise AsException("Nom de concept deja defini : %s" % sdnom)
348 # ATTENTION : Il ne faut pas ajouter sd dans sds car il s y trouve deja.
349 # Ajoute a la creation (appel de reg_sd).
350 self.sds_dict[sdnom]=sd
353 # En plus si restrict vaut 'non', on insere le concept dans le contexte du JDC
354 if restrict == 'non':
355 self.g_context[sdnom]=sd
359 Methode appelee dans l __init__ d un ASSD lors de sa creation
363 return self.o_register(sd)
365 def delete_concept_after_etape(self,etape,sd):
367 Met à jour les étapes du JDC qui sont après etape suite à
368 la disparition du concept sd
370 # Cette methode est définie dans le noyau mais ne sert que pendant
371 # la phase de creation des etapes et des concepts. Il n'y a aucun
372 # traitement particulier à réaliser.
373 # Dans d'autres conditions, il faut surcharger cette méthode
377 N_OBJECT.OBJECT.supprime(self)
378 for etape in self.etapes:
381 def get_file(self,unite=None,fic_origine=''):
383 Retourne le nom du fichier correspondant à un numero d'unité
384 logique (entier) ainsi que le source contenu dans le fichier
387 # Si le JDC est relié à une application maitre, on délègue la recherche
388 file,text= self.appli.get_file(unite,fic_origine)
392 if os.path.exists("fort."+str(unite)):
393 file= "fort."+str(unite)
395 raise AsException("Impossible de trouver le fichier correspondant"
396 " a l unite %s" % unite)
397 if not os.path.exists(file):
398 raise AsException("%s n'est pas un fichier existant" % unite)
402 if file == None : return None,None
403 text=string.replace(text,'\r\n','\n')
404 linecache.cache[file]=0,0,string.split(text,'\n'),file
407 def set_par_lot(self,par_lot):
409 Met le mode de traitement a PAR LOT
410 ou a COMMANDE par COMMANDE
411 en fonction de la valeur du mot cle PAR_LOT et
412 du contexte : application maitre ou pas
414 if self.appli == None:
415 # Pas d application maitre
418 # Avec application maitre
421 def accept(self,visitor):
423 Cette methode permet de parcourir l'arborescence des objets
424 en utilisant le pattern VISITEUR
426 visitor.visitJDC(self)
430 Cette methode a pour fonction d'ouvrir un interpreteur
431 pour que l'utilisateur entre des commandes interactivement
433 CONTEXT.set_current_step(self)
435 # Le module nommage utilise le module linecache pour accéder
436 # au source des commandes du jeu de commandes.
437 # Dans le cas d'un fichier, on accède au contenu de ce fichier
438 # Dans le cas de la console interactive, il faut pouvoir accéder
439 # aux commandes qui sont dans le buffer de la console
440 import linecache,code
441 console= code.InteractiveConsole(self.g_context,filename="<console>")
442 linecache.cache["<console>"]=0,0,console.buffer,"<console>"
443 banner="""***********************************************
444 * Interpreteur interactif %s
445 ***********************************************""" % self.code
446 console.interact(banner)
449 CONTEXT.unset_current_step()
451 def get_contexte_avant(self,etape):
453 Retourne le dictionnaire des concepts connus avant etape
454 On tient compte des commandes qui modifient le contexte
455 comme DETRUIRE ou les macros
456 Si etape == None, on retourne le contexte en fin de JDC
458 # L'étape courante pour laquelle le contexte a été calculé est
459 # mémorisée dans self.index_etape_courante
460 # XXX on pourrait faire mieux dans le cas PAR_LOT="NON" : en
462 # courante pendant le processus de construction des étapes.
463 # Si on insère des commandes (par ex, dans EFICAS), il faut préalablement
464 # remettre ce pointeur à 0
466 index_etape = self.index_etapes[etape]
468 index_etape=len(self.etapes)
469 if index_etape >= self.index_etape_courante:
470 # On calcule le contexte en partant du contexte existant
471 d=self.current_context
472 if self.index_etape_courante==0 and self.context_ini:
473 d.update(self.context_ini)
474 liste_etapes=self.etapes[self.index_etape_courante:index_etape]
476 d=self.current_context={}
478 d.update(self.context_ini)
479 liste_etapes=self.etapes
481 for e in liste_etapes:
486 self.index_etape_courante=index_etape
489 def get_global_contexte(self):
490 return self.g_context.copy()
492 def get_cmd(self,nomcmd):
494 Méthode pour recuperer la definition d'une commande
495 donnee par son nom dans les catalogues declares
498 for cata in self.cata:
499 if hasattr(cata,nomcmd):
500 return getattr(cata,nomcmd)
502 def append_reset(self,etape):
504 Ajoute une etape provenant d'un autre jdc a la liste des etapes
505 et remet à jour la parenté de l'étape et des concepts
507 self.etapes.append(etape)
508 self.index_etapes[etape] = len(self.etapes) - 1
510 etape.reset_jdc(self)