1 #@ MODIF N_JDC Noyau DATE 16/11/2009 AUTEUR COURTOIS M.COURTOIS
2 # -*- coding: iso-8859-1 -*-
3 # RESPONSABLE COURTOIS M.COURTOIS
4 # CONFIGURATION MANAGEMENT OF EDF VERSION
5 # ======================================================================
6 # COPYRIGHT (C) 1991 - 2002 EDF R&D WWW.CODE-ASTER.ORG
7 # THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
8 # IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
9 # THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR
10 # (AT YOUR OPTION) ANY LATER VERSION.
12 # THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT
13 # WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF
14 # MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU
15 # GENERAL PUBLIC LICENSE FOR MORE DETAILS.
17 # YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE
18 # ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,
19 # 1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.
22 # ======================================================================
26 Ce module contient la classe JDC qui sert à interpréter un jeu de commandes
30 import os,string,traceback
31 import types,sys,linecache
36 from N_Exception import AsException
37 from N_ASSD import ASSD
42 MemoryErrorMsg = """MemoryError :
44 En général, cette erreur se produit car la mémoire utilisée hors du fortran
45 (jeveux) est importante.
48 - le calcul produit de gros objets Python dans une macro-commande ou
49 dans le jeu de commande lui-même,
50 - le calcul appelle un solveur (MUMPS par exemple) ou un outil externe
51 qui a besoin de mémoire hors jeveux,
52 - utilisation de jeveux dynamique,
56 - distinguer la mémoire limite du calcul (case "Mémoire totale" de astk)
57 de la mémoire réservée à jeveux (case "dont Aster"), le reste étant
58 disponible pour les allocations dynamiques.
63 class JDC(N_OBJECT.OBJECT):
65 Cette classe interprete un jeu de commandes fourni sous
66 la forme d'une chaine de caractères
70 Attributs d'instance :
82 from N_utils import SEP
84 def __init__(self,definition=None,procedure=None,cata=None,
85 cata_ord_dico=None,parent=None,
86 nom='SansNom',appli=None,context_ini=None,**args):
87 self.procedure=procedure
88 self.definition = definition
90 if type(self.cata) != types.TupleType and cata != None:
91 self.cata=(self.cata,)
92 self.cata_ordonne_dico=cata_ord_dico
96 self.context_ini=context_ini
97 # On conserve les arguments supplémentaires. Il est possible de passer
98 # des informations globales au JDC par ce moyen. Il pourrait etre plus
99 # sur de mettre en place le mecanisme des mots-cles pour verifier la
100 # validité des valeurs passées.
102 # On initialise avec les parametres de la definition puis on
103 # update avec ceux du JDC
104 self.args=self.definition.args
105 self.args.update(args)
110 self.regles=definition.regles
111 self.code = definition.code
116 # Creation de l objet compte rendu pour collecte des erreurs
118 self.cr = self.CR(debut = "CR phase d'initialisation",
119 fin = "fin CR phase d'initialisation")
120 # on met le jdc lui-meme dans le context global pour l'avoir sous
121 # l'etiquette "jdc" dans le fichier de commandes
122 self.g_context={ 'jdc' : self }
123 # Liste pour stocker tous les concepts produits dans le JDC
125 # Dictionnaire pour stocker tous les concepts du JDC (acces rapide par le nom)
128 self.index_etapes = {}
130 self.current_context={}
131 self.condition_context={}
132 self.index_etape_courante=0
133 self.UserError="UserError"
138 Cette methode compile la chaine procedure
139 Si des erreurs se produisent, elles sont consignées dans le
143 if self.appli != None :
144 self.appli.affiche_infos('Compilation du fichier de commandes en cours ...')
145 self.proc_compile=compile(self.procedure,self.nom,'exec')
146 except SyntaxError, e:
147 if CONTEXT.debug : traceback.print_exc()
148 l=traceback.format_exception_only(SyntaxError,e)
149 self.cr.exception("Compilation impossible : "+string.join(l))
150 except MemoryError, e:
151 self.cr.exception(MemoryErrorMsg)
152 except SystemError, e:
153 erreurs_connues = """
155 - offset too large : liste trop longue derrière un mot-clé.
156 Solution : liste = (valeurs, ..., )
159 l=traceback.format_exception_only(SystemError,e)
160 l.append(erreurs_connues)
161 self.cr.exception("Compilation impossible : " + ''.join(l))
164 def exec_compile(self):
166 Cette méthode execute le jeu de commandes compilé dans le contexte
167 self.g_context de l'objet JDC
169 CONTEXT.set_current_step(self)
170 # Le module nommage utilise le module linecache pour accéder
171 # au source des commandes du jeu de commandes.
172 # Dans le cas d'un fichier, on accède au contenu de ce fichier
173 # Dans le cas d'une chaine de caractères il faut accéder
174 # aux commandes qui sont dans la chaine
176 linecache.cache[self.nom]=0,0,string.split(self.procedure,'\n'),self.nom
178 exec self.exec_init in self.g_context
179 for obj_cata in self.cata:
180 if type(obj_cata) == types.ModuleType :
181 init2 = "from "+obj_cata.__name__+" import *"
182 exec init2 in self.g_context
184 # Initialisation du contexte global pour l'évaluation des conditions de BLOC
185 # On utilise une copie de l'initialisation du contexte du jdc
186 self.condition_context=self.g_context.copy()
188 # Si l'attribut context_ini n'est pas vide, on ajoute au contexte global
189 # le contexte initial (--> permet d'évaluer un JDC en récupérant un contexte
190 # d'un autre par exemple)
191 if self.context_ini :
192 self.g_context.update(self.context_ini)
193 # Update du dictionnaire des concepts
194 for sdnom,sd in self.context_ini.items():
195 if isinstance(sd,ASSD):self.sds_dict[sdnom]=sd
197 if self.appli != None :
198 self.appli.affiche_infos('Interpretation du fichier de commandes en cours ...')
199 # On sauve le contexte pour garder la memoire des constantes
200 # En mode edition (EFICAS) ou lors des verifications le contexte
202 # mais les constantes sont perdues
203 self.const_context=self.g_context
204 exec self.proc_compile in self.g_context
206 CONTEXT.unset_current_step()
207 if self.appli != None : self.appli.affiche_infos('')
210 # Exception utilise pour interrompre un jeu
211 # de commandes avant la fin
212 # Fonctionnement normal, ne doit pas etre considere comme une erreur
213 CONTEXT.unset_current_step()
214 self.affiche_fin_exec()
215 self.traiter_fin_exec('commande')
217 except AsException,e:
218 # une erreur a ete identifiee
220 traceback.print_exc()
221 # l'exception a été récupérée avant (où, comment ?),
222 # donc on cherche dans le texte
224 if txt.find('MemoryError') >= 0:
226 self.cr.exception(txt)
227 CONTEXT.unset_current_step()
230 etype, value, tb = sys.exc_info()
231 l= traceback.extract_tb(tb)
232 s= traceback.format_exception_only("Erreur de nom",e)[0][:-1]
233 message = "erreur de syntaxe, %s ligne %d" % (s,l[-1][1])
235 traceback.print_exc()
236 self.cr.exception(message)
237 CONTEXT.unset_current_step()
239 except self.UserError,exc_val:
240 self.traiter_user_exception(exc_val)
241 CONTEXT.unset_current_step()
242 self.affiche_fin_exec()
243 self.traiter_fin_exec('commande')
247 # sys_exc_typ,sys_exc_value,sys_exc_frame = sys_exc.info()
248 # (tuple de 3 éléments)
249 if CONTEXT.debug : traceback.print_exc()
251 exc_typ,exc_val,exc_fr=sys.exc_info()
252 l=traceback.format_exception(exc_typ,exc_val,exc_fr)
253 self.cr.exception("erreur non prevue et non traitee prevenir la maintenance "+
254 self.nom+'\n'+ string.join(l))
255 del exc_typ,exc_val,exc_fr
256 CONTEXT.unset_current_step()
258 def affiche_fin_exec(self):
260 Cette methode realise l'affichage final des statistiques de temps
261 apres l'execution de toutes
262 les commandes en mode commande par commande ou par lot
263 Elle doit etre surchargee pour en introduire un
267 def traiter_fin_exec(self,mode,etape=None):
269 Cette methode realise un traitement final apres l'execution de toutes
270 les commandes en mode commande par commande ou par lot
271 Par defaut il n'y a pas de traitement. Elle doit etre surchargee
272 pour en introduire un
274 print "FIN D'EXECUTION",mode,etape
276 def traiter_user_exception(self,exc_val):
277 """Cette methode realise un traitement sur les exceptions utilisateur
278 Par defaut il n'y a pas de traitement. La méthode doit etre
279 surchargée pour en introduire un.
283 def register(self,etape):
285 Cette méthode ajoute etape dans la liste des etapes : self.etapes
286 et retourne un numéro d'enregistrement
288 self.etapes.append(etape)
289 self.index_etapes[etape] = len(self.etapes) - 1
290 return self.g_register(etape)
292 def o_register(self,sd):
294 Retourne un identificateur pour concept
297 nom=sd.idracine + self.SEP + `self.nsd`
300 def g_register(self,etape):
302 Retourne un identificateur pour etape
304 self.nstep=self.nstep+1
305 idetape=etape.idracine + self.SEP + `self.nstep`
308 def create_sdprod(self,etape,nomsd):
310 Cette methode doit fabriquer le concept produit retourne
311 par l'etape etape et le nommer.
313 Elle est appelée à l'initiative de l'etape
314 pendant le processus de construction de cette etape :
315 methode __call__ de la classe CMD (OPER ou MACRO)
317 Ce travail est réalisé par le contexte supérieur
318 (etape.parent) car dans certains cas, le concept ne doit
319 pas etre fabriqué mais l'etape doit simplement utiliser
320 un concept préexistant.
323 - Cas 1 : etape.reuse != None : le concept est réutilisé
324 - Cas 2 : l'étape appartient à une macro qui a déclaré un
325 concept de sortie qui doit etre produit par cette
327 Dans le cas du JDC, le deuxième cas ne peut pas se produire.
329 sd= etape.get_sd_prod()
330 if sd != None and (etape.definition.reentrant == 'n' or etape.reuse is None) :
331 # ATTENTION : On ne nomme la SD que dans le cas de non reutilisation
332 # d un concept. Commande non reentrante ou reuse absent.
333 self.NommerSdprod(sd,nomsd)
336 def NommerSdprod(self,sd,sdnom,restrict='non'):
338 Nomme la SD apres avoir verifie que le nommage est possible : nom
340 Si le nom est deja utilise, leve une exception
341 Met le concept créé dans le concept global g_context
343 if CONTEXT.debug : print "JDC.NommerSdprod ",sd,sdnom
345 o=self.sds_dict.get(sdnom,None)
346 if isinstance(o,ASSD):
347 raise AsException("Nom de concept deja defini : %s" % sdnom)
349 # ATTENTION : Il ne faut pas ajouter sd dans sds car il s y trouve deja.
350 # Ajoute a la creation (appel de reg_sd).
351 self.sds_dict[sdnom]=sd
354 # En plus si restrict vaut 'non', on insere le concept dans le contexte du JDC
355 if restrict == 'non':
356 self.g_context[sdnom]=sd
360 Methode appelee dans l __init__ d un ASSD lors de sa creation
364 return self.o_register(sd)
366 def delete_concept_after_etape(self,etape,sd):
368 Met à jour les étapes du JDC qui sont après etape suite à
369 la disparition du concept sd
371 # Cette methode est définie dans le noyau mais ne sert que pendant
372 # la phase de creation des etapes et des concepts. Il n'y a aucun
373 # traitement particulier à réaliser.
374 # Dans d'autres conditions, il faut surcharger cette méthode
378 N_OBJECT.OBJECT.supprime(self)
379 for etape in self.etapes:
382 def get_file(self,unite=None,fic_origine=''):
384 Retourne le nom du fichier correspondant à un numero d'unité
385 logique (entier) ainsi que le source contenu dans le fichier
388 # Si le JDC est relié à une application maitre, on délègue la recherche
389 file,text= self.appli.get_file(unite,fic_origine)
393 if os.path.exists("fort."+str(unite)):
394 file= "fort."+str(unite)
396 raise AsException("Impossible de trouver le fichier correspondant"
397 " a l unite %s" % unite)
398 if not os.path.exists(file):
399 raise AsException("%s n'est pas un fichier existant" % unite)
403 if file == None : return None,None
404 text=string.replace(text,'\r\n','\n')
405 linecache.cache[file]=0,0,string.split(text,'\n'),file
408 def set_par_lot(self,par_lot):
410 Met le mode de traitement a PAR LOT
411 ou a COMMANDE par COMMANDE
412 en fonction de la valeur du mot cle PAR_LOT et
413 du contexte : application maitre ou pas
415 if self.appli == None:
416 # Pas d application maitre
419 # Avec application maitre
422 def accept(self,visitor):
424 Cette methode permet de parcourir l'arborescence des objets
425 en utilisant le pattern VISITEUR
427 visitor.visitJDC(self)
431 Cette methode a pour fonction d'ouvrir un interpreteur
432 pour que l'utilisateur entre des commandes interactivement
434 CONTEXT.set_current_step(self)
436 # Le module nommage utilise le module linecache pour accéder
437 # au source des commandes du jeu de commandes.
438 # Dans le cas d'un fichier, on accède au contenu de ce fichier
439 # Dans le cas de la console interactive, il faut pouvoir accéder
440 # aux commandes qui sont dans le buffer de la console
441 import linecache,code
442 console= code.InteractiveConsole(self.g_context,filename="<console>")
443 linecache.cache["<console>"]=0,0,console.buffer,"<console>"
444 banner="""***********************************************
445 * Interpreteur interactif %s
446 ***********************************************""" % self.code
447 console.interact(banner)
450 CONTEXT.unset_current_step()
452 def get_contexte_avant(self,etape):
454 Retourne le dictionnaire des concepts connus avant etape
455 On tient compte des commandes qui modifient le contexte
456 comme DETRUIRE ou les macros
457 Si etape == None, on retourne le contexte en fin de JDC
459 # L'étape courante pour laquelle le contexte a été calculé est
460 # mémorisée dans self.index_etape_courante
461 # XXX on pourrait faire mieux dans le cas PAR_LOT="NON" : en
463 # courante pendant le processus de construction des étapes.
464 # Si on insère des commandes (par ex, dans EFICAS), il faut préalablement
465 # remettre ce pointeur à 0
467 index_etape = self.index_etapes[etape]
469 index_etape=len(self.etapes)
470 if index_etape >= self.index_etape_courante:
471 # On calcule le contexte en partant du contexte existant
472 d=self.current_context
473 if self.index_etape_courante==0 and self.context_ini:
474 d.update(self.context_ini)
475 liste_etapes=self.etapes[self.index_etape_courante:index_etape]
477 d=self.current_context={}
479 d.update(self.context_ini)
480 liste_etapes=self.etapes
482 for e in liste_etapes:
487 self.index_etape_courante=index_etape
490 def get_global_contexte(self):
491 return self.g_context.copy()
494 def get_contexte_courant(self, etape_courante=None):
496 Retourne le contexte tel qu'il est (ou 'sera' si on est en phase
497 de construction) au moment de l'exécution de l'étape courante.
499 if etape_courante is None:
500 etape_courante = CONTEXT.get_current_step()
501 return self.get_contexte_avant(etape_courante)
504 def get_concept(self, nomsd):
506 Méthode pour recuperer un concept à partir de son nom
508 return self.get_contexte_courant().get(nomsd.strip(), None)
510 def del_concept(self, nomsd):
512 Méthode pour supprimer la référence d'un concept dans le sds_dict.
513 Ne détruire pas le concept (différent de supprime).
516 del self.sds_dict[nomsd.strip()]
521 def get_cmd(self,nomcmd):
523 Méthode pour recuperer la definition d'une commande
524 donnee par son nom dans les catalogues declares
527 for cata in self.cata:
528 if hasattr(cata,nomcmd):
529 return getattr(cata,nomcmd)
531 def append_reset(self,etape):
533 Ajoute une etape provenant d'un autre jdc a la liste des etapes
534 et remet à jour la parenté de l'étape et des concepts
536 self.etapes.append(etape)
537 self.index_etapes[etape] = len(self.etapes) - 1
539 etape.reset_jdc(self)
541 def sd_accessible(self):
542 """On peut acceder aux "valeurs" (jeveux) des ASSD si le JDC est en PAR_LOT="NON".
544 if CONTEXT.debug: print ' `- JDC sd_accessible : PAR_LOT =', self.par_lot
545 return self.par_lot == 'NON'