1 #@ MODIF N_JDC Noyau DATE 30/05/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 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
38 class JDC(N_OBJECT.OBJECT):
40 Cette classe interprete un jeu de commandes fourni sous
41 la forme d'une chaine de caractères
45 Attributs d'instance :
57 from N_utils import SEP
59 def __init__(self,definition=None,procedure=None,cata=None,
60 cata_ord_dico=None,parent=None,
61 nom='SansNom',appli=None,context_ini=None,**args):
62 self.procedure=procedure
63 self.definition = definition
65 if type(self.cata) != types.TupleType and cata != None:
66 self.cata=(self.cata,)
67 self.cata_ordonne_dico=cata_ord_dico
71 self.context_ini=context_ini
72 # On conserve les arguments supplémentaires. Il est possible de passer
73 # des informations globales au JDC par ce moyen. Il pourrait etre plus
74 # sur de mettre en place le mecanisme des mots-cles pour verifier la
75 # validité des valeurs passées.
77 # On initialise avec les parametres de la definition puis on
78 # update avec ceux du JDC
79 self.args=self.definition.args
80 self.args.update(args)
85 self.regles=definition.regles
86 self.code = definition.code
91 # Creation de l objet compte rendu pour collecte des erreurs
93 self.cr = self.CR(debut = "CR phase d'initialisation",
94 fin = "fin CR phase d'initialisation")
96 # Liste pour stocker tous les concepts produits dans le JDC
98 # Dictionnaire pour stocker tous les concepts du JDC (acces rapide par le nom)
102 self.current_context={}
103 self.condition_context={}
104 self.index_etape_courante=0
105 self.UserError="UserError"
110 Cette methode compile la chaine procedure
111 Si des erreurs se produisent, elles sont consignées dans le
115 if self.appli != None :
116 self.appli.affiche_infos('Compilation du fichier de commandes en cours ...')
117 self.proc_compile=compile(self.procedure,self.nom,'exec')
118 except SyntaxError,e:
119 if CONTEXT.debug : traceback.print_exc()
120 l=traceback.format_exception_only(SyntaxError,e)
121 self.cr.exception("Compilation impossible : "+string.join(l))
124 def exec_compile(self):
126 Cette méthode execute le jeu de commandes compilé dans le contexte
127 self.g_context de l'objet JDC
129 CONTEXT.set_current_step(self)
130 # Le module nommage utilise le module linecache pour accéder
131 # au source des commandes du jeu de commandes.
132 # Dans le cas d'un fichier, on accède au contenu de ce fichier
133 # Dans le cas d'une chaine de caractères il faut accéder
134 # aux commandes qui sont dans la chaine
136 linecache.cache[self.nom]=0,0,string.split(self.procedure,'\n'),self.nom
138 exec self.exec_init in self.g_context
139 for obj_cata in self.cata:
140 if type(obj_cata) == types.ModuleType :
141 init2 = "from "+obj_cata.__name__+" import *"
142 exec init2 in self.g_context
144 # Initialisation du contexte global pour l'évaluation des conditions de BLOC
145 # On utilise une copie de l'initialisation du contexte du jdc
146 self.condition_context=self.g_context.copy()
148 # Si l'attribut context_ini n'est pas vide, on ajoute au contexte global
149 # le contexte initial (--> permet d'évaluer un JDC en récupérant un contexte
150 # d'un autre par exemple)
151 if self.context_ini :
152 self.g_context.update(self.context_ini)
153 # Update du dictionnaire des concepts
154 for sdnom,sd in self.context_ini.items():
155 if isinstance(sd,ASSD):self.sds_dict[sdnom]=sd
157 if self.appli != None :
158 self.appli.affiche_infos('Interprétation du fichier de commandes en cours ...')
159 # On sauve le contexte pour garder la memoire des constantes
160 # En mode edition (EFICAS) ou lors des verifications le contexte
162 # mais les constantes sont perdues
163 self.const_context=self.g_context
164 exec self.proc_compile in self.g_context
166 CONTEXT.unset_current_step()
167 if self.appli != None : self.appli.affiche_infos('')
170 # Exception utilise pour interrompre un jeu
171 # de commandes avant la fin
172 # Fonctionnement normal, ne doit pas etre considere comme une erreur
173 CONTEXT.unset_current_step()
174 self.affiche_fin_exec()
175 self.traiter_fin_exec('commande')
177 except AsException,e:
178 # une erreur a ete identifiee
180 traceback.print_exc()
181 self.cr.exception(str(e))
182 CONTEXT.unset_current_step()
185 etype, value, tb = sys.exc_info()
186 l= traceback.extract_tb(tb)
187 s= traceback.format_exception_only("Erreur de nom",e)[0][:-1]
188 message = "erreur de syntaxe, %s ligne %d" % (s,l[-1][1])
190 traceback.print_exc()
191 self.cr.exception(message)
192 CONTEXT.unset_current_step()
194 except self.UserError,exc_val:
195 self.traiter_user_exception(exc_val)
196 CONTEXT.unset_current_step()
197 self.affiche_fin_exec()
198 self.traiter_fin_exec('commande')
202 # sys_exc_typ,sys_exc_value,sys_exc_frame = sys_exc.info()
203 # (tuple de 3 éléments)
204 if CONTEXT.debug : traceback.print_exc()
206 exc_typ,exc_val,exc_fr=sys.exc_info()
207 l=traceback.format_exception(exc_typ,exc_val,exc_fr)
208 self.cr.exception("erreur non prevue et non traitee prevenir la maintenance "+
209 self.nom+'\n'+ string.join(l))
210 del exc_typ,exc_val,exc_fr
211 CONTEXT.unset_current_step()
213 def affiche_fin_exec(self):
215 Cette methode realise l'affichage final des statistiques de temps
216 apres l'execution de toutes
217 les commandes en mode commande par commande ou par lot
218 Elle doit etre surchargee pour en introduire un
222 def traiter_fin_exec(self,mode,etape=None):
224 Cette methode realise un traitement final apres l'execution de toutes
225 les commandes en mode commande par commande ou par lot
226 Par defaut il n'y a pas de traitement. Elle doit etre surchargee
227 pour en introduire un
229 print "FIN D'EXECUTION",mode,etape
231 def traiter_user_exception(self,exc_val):
232 """Cette methode realise un traitement sur les exceptions utilisateur
233 Par defaut il n'y a pas de traitement. La méthode doit etre
234 surchargée pour en introduire un.
238 def register(self,etape):
240 Cette méthode ajoute etape dans la liste des etapes : self.etapes
241 et retourne un numéro d'enregistrement
243 self.etapes.append(etape)
244 return self.g_register(etape)
246 def o_register(self,sd):
248 Retourne un identificateur pour concept
251 nom=sd.idracine + self.SEP + `self.nsd`
254 def g_register(self,etape):
256 Retourne un identificateur pour etape
258 self.nstep=self.nstep+1
259 idetape=etape.idracine + self.SEP + `self.nstep`
262 def create_sdprod(self,etape,nomsd):
264 Cette methode doit fabriquer le concept produit retourne
265 par l'etape etape et le nommer.
267 Elle est appelée à l'initiative de l'etape
268 pendant le processus de construction de cette etape :
269 methode __call__ de la classe CMD (OPER ou MACRO)
271 Ce travail est réalisé par le contexte supérieur
272 (etape.parent) car dans certains cas, le concept ne doit
273 pas etre fabriqué mais l'etape doit simplement utiliser
274 un concept préexistant.
277 - Cas 1 : etape.reuse != None : le concept est réutilisé
278 - Cas 2 : l'étape appartient à une macro qui a déclaré un
279 concept de sortie qui doit etre produit par cette
281 Dans le cas du JDC, le deuxième cas ne peut pas se produire.
283 sd= etape.get_sd_prod()
284 if sd != None and (etape.definition.reentrant == 'n' or etape.reuse is None) :
285 # ATTENTION : On ne nomme la SD que dans le cas de non reutilisation
286 # d un concept. Commande non reentrante ou reuse absent.
287 self.NommerSdprod(sd,nomsd)
290 def NommerSdprod(self,sd,sdnom,restrict='non'):
292 Nomme la SD apres avoir verifie que le nommage est possible : nom
294 Si le nom est deja utilise, leve une exception
295 Met le concept créé dans le concept global g_context
297 if CONTEXT.debug : print "JDC.NommerSdprod ",sd,sdnom
299 o=self.sds_dict.get(sdnom,None)
300 if isinstance(o,ASSD):
301 raise AsException("Nom de concept deja defini : %s" % sdnom)
303 # ATTENTION : Il ne faut pas ajouter sd dans sds car il s y trouve deja.
304 # Ajoute a la creation (appel de reg_sd).
305 self.sds_dict[sdnom]=sd
308 # En plus si restrict vaut 'non', on insere le concept dans le contexte du JDC
309 if restrict == 'non':
310 self.g_context[sdnom]=sd
314 Methode appelee dans l __init__ d un ASSD lors de sa creation
318 return self.o_register(sd)
320 def delete_concept_after_etape(self,etape,sd):
322 Met à jour les étapes du JDC qui sont après etape suite à
323 la disparition du concept sd
325 # Cette methode est définie dans le noyau mais ne sert que pendant
326 # la phase de creation des etapes et des concepts. Il n'y a aucun
327 # traitement particulier à réaliser.
328 # Dans d'autres conditions, il faut surcharger cette méthode
332 N_OBJECT.OBJECT.supprime(self)
333 for etape in self.etapes:
336 def get_file(self,unite=None,fic_origine=''):
338 Retourne le nom du fichier correspondant à un numero d'unité
339 logique (entier) ainsi que le source contenu dans le fichier
342 # Si le JDC est relié à une application maitre, on délègue la recherche
343 file,text= self.appli.get_file(unite,fic_origine)
347 if os.path.exists("fort."+str(unite)):
348 file= "fort."+str(unite)
350 raise AsException("Impossible de trouver le fichier correspondant"
351 " a l unite %s" % unite)
352 if not os.path.exists(file):
353 raise AsException("%s n'est pas un fichier existant" % unite)
357 if file == None : return None,None
358 text=string.replace(text,'\r\n','\n')
359 linecache.cache[file]=0,0,string.split(text,'\n'),file
362 def set_par_lot(self,par_lot):
364 Met le mode de traitement a PAR LOT
365 ou a COMMANDE par COMMANDE
366 en fonction de la valeur du mot cle PAR_LOT et
367 du contexte : application maitre ou pas
369 if self.appli == None:
370 # Pas d application maitre
373 # Avec application maitre
376 def accept(self,visitor):
378 Cette methode permet de parcourir l'arborescence des objets
379 en utilisant le pattern VISITEUR
381 visitor.visitJDC(self)
385 Cette methode a pour fonction d'ouvrir un interpreteur
386 pour que l'utilisateur entre des commandes interactivement
388 CONTEXT.set_current_step(self)
390 # Le module nommage utilise le module linecache pour accéder
391 # au source des commandes du jeu de commandes.
392 # Dans le cas d'un fichier, on accède au contenu de ce fichier
393 # Dans le cas de la console interactive, il faut pouvoir accéder
394 # aux commandes qui sont dans le buffer de la console
395 import linecache,code
396 console= code.InteractiveConsole(self.g_context,filename="<console>")
397 linecache.cache["<console>"]=0,0,console.buffer,"<console>"
398 banner="""***********************************************
399 * Interpreteur interactif %s
400 ***********************************************""" % self.code
401 console.interact(banner)
404 CONTEXT.unset_current_step()
406 def get_contexte_avant(self,etape):
408 Retourne le dictionnaire des concepts connus avant etape
409 On tient compte des commandes qui modifient le contexte
410 comme DETRUIRE ou les macros
411 Si etape == None, on retourne le contexte en fin de JDC
413 # L'étape courante pour laquelle le contexte a été calculé est
414 # mémorisée dans self.index_etape_courante
415 # XXX on pourrait faire mieux dans le cas PAR_LOT="NON" : en
417 # courante pendant le processus de construction des étapes.
418 # Si on insère des commandes (par ex, dans EFICAS), il faut préalablement
419 # remettre ce pointeur à 0
421 index_etape=self.etapes.index(etape)
423 index_etape=len(self.etapes)
424 if index_etape >= self.index_etape_courante:
425 # On calcule le contexte en partant du contexte existant
426 d=self.current_context
427 if self.index_etape_courante==0 and self.context_ini:
428 d.update(self.context_ini)
429 liste_etapes=self.etapes[self.index_etape_courante:index_etape]
431 d=self.current_context={}
432 if self.context_ini:d.update(self.context_ini)
433 liste_etapes=self.etapes
435 for e in liste_etapes:
440 self.index_etape_courante=index_etape
443 def get_global_contexte(self):
444 return self.g_context.copy()
446 def get_cmd(self,nomcmd):
448 Méthode pour recuperer la definition d'une commande
449 donnee par son nom dans les catalogues declares
452 for cata in self.cata:
453 if hasattr(cata,nomcmd):
454 return getattr(cata,nomcmd)
456 def append_reset(self,etape):
458 Ajoute une etape provenant d'un autre jdc a la liste des etapes
459 et remet à jour la parenté de l'étape et des concepts
461 self.etapes.append(etape)
463 etape.reset_jdc(self)