1 #@ MODIF N_JDC Noyau DATE 05/11/2003 AUTEUR CAMBIER S.CAMBIER
2 # CONFIGURATION MANAGEMENT OF EDF VERSION
3 # ======================================================================
4 # COPYRIGHT (C) 1991 - 2002 EDF R&D WWW.CODE-ASTER.ORG
5 # THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
6 # IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
7 # THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR
8 # (AT YOUR OPTION) ANY LATER VERSION.
10 # THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT
11 # WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF
12 # MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU
13 # GENERAL PUBLIC LICENSE FOR MORE DETAILS.
15 # YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE
16 # ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,
17 # 1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.
20 # ======================================================================
22 Ce module contient la classe JDC qui sert à interpréter un jeu de commandes
26 import os,string,traceback
27 import types,sys,linecache
32 from N_Exception import AsException
33 from N_ASSD import ASSD
35 class JDC(N_OBJECT.OBJECT):
37 Cette classe interprete un jeu de commandes fourni sous
38 la forme d'une chaine de caractères
42 Attributs d'instance :
54 from N_utils import SEP
56 def __init__(self,definition=None,procedure=None,cata=None,
57 cata_ord_dico=None,parent=None,
58 nom='SansNom',appli=None,context_ini=None,**args):
59 self.procedure=procedure
60 self.definition = definition
62 if type(self.cata) != types.TupleType and cata != None:
63 self.cata=(self.cata,)
64 self.cata_ordonne_dico=cata_ord_dico
68 self.context_ini=context_ini
69 # On conserve les arguments supplémentaires. Il est possible de passer
70 # des informations globales au JDC par ce moyen. Il pourrait etre plus
71 # sur de mettre en place le mecanisme des mots-cles pour verifier la
72 # validité des valeurs passées.
74 # On initialise avec les parametres de la definition puis on
75 # update avec ceux du JDC
76 self.args=self.definition.args
77 self.args.update(args)
82 self.regles=definition.regles
83 self.code = definition.code
88 # Creation de l objet compte rendu pour collecte des erreurs
90 self.cr = self.CR(debut = "CR phase d'initialisation",
91 fin = "fin CR phase d'initialisation")
93 # Liste pour stocker tous les concepts produits dans le JDC
95 # Dictionnaire pour stocker tous les concepts du JDC (acces rapide par le nom)
99 self.current_context={}
100 self.condition_context={}
101 self.index_etape_courante=0
102 self.UserError="UserError"
107 Cette methode compile la chaine procedure
108 Si des erreurs se produisent, elles sont consignées dans le
112 if self.appli != None :
113 self.appli.affiche_infos('Compilation du fichier de commandes en cours ...')
114 self.proc_compile=compile(self.procedure,self.nom,'exec')
115 except SyntaxError,e:
116 if CONTEXT.debug : traceback.print_exc()
117 l=traceback.format_exception_only(SyntaxError,e)
118 self.cr.exception("Compilation impossible : "+string.join(l))
121 def exec_compile(self):
123 Cette méthode execute le jeu de commandes compilé dans le contexte
124 self.g_context de l'objet JDC
126 CONTEXT.set_current_step(self)
127 # Le module nommage utilise le module linecache pour accéder
128 # au source des commandes du jeu de commandes.
129 # Dans le cas d'un fichier, on accède au contenu de ce fichier
130 # Dans le cas d'une chaine de caractères il faut accéder
131 # aux commandes qui sont dans la chaine
133 linecache.cache[self.nom]=0,0,string.split(self.procedure,'\n'),self.nom
135 exec self.exec_init in self.g_context
136 for obj_cata in self.cata:
137 if type(obj_cata) == types.ModuleType :
138 init2 = "from "+obj_cata.__name__+" import *"
139 exec init2 in self.g_context
141 # Initialisation du contexte global pour l'évaluation des conditions de BLOC
142 # On utilise une copie de l'initialisation du contexte du jdc
143 self.condition_context=self.g_context.copy()
145 # Si l'attribut context_ini n'est pas vide, on ajoute au contexte global
146 # le contexte initial (--> permet d'évaluer un JDC en récupérant un contexte
147 # d'un autre par exemple)
148 if self.context_ini :
149 self.g_context.update(self.context_ini)
150 # Update du dictionnaire des concepts
151 for sdnom,sd in self.context_ini.items():
152 if isinstance(sd,ASSD):self.sds_dict[sdnom]=sd
154 if self.appli != None :
155 self.appli.affiche_infos('Interprétation du fichier de commandes en cours ...')
156 # On sauve le contexte pour garder la memoire des constantes
157 # En mode edition (EFICAS) ou lors des verifications le contexte
159 # mais les constantes sont perdues
160 self.const_context=self.g_context
161 exec self.proc_compile in self.g_context
163 CONTEXT.unset_current_step()
164 if self.appli != None : self.appli.affiche_infos('')
167 # Exception utilise pour interrompre un jeu
168 # de commandes avant la fin
169 # Fonctionnement normal, ne doit pas etre considere comme une erreur
170 CONTEXT.unset_current_step()
172 except AsException,e:
173 # une erreur a ete identifiee
175 traceback.print_exc()
176 self.cr.exception(str(e))
177 CONTEXT.unset_current_step()
180 etype, value, tb = sys.exc_info()
181 l= traceback.extract_tb(tb)
182 s= traceback.format_exception_only("Erreur de nom",e)[0][:-1]
183 message = "erreur de syntaxe, %s ligne %d" % (s,l[-1][1])
185 traceback.print_exc()
186 self.cr.exception(message)
187 CONTEXT.unset_current_step()
189 except self.UserError,exc_val:
190 self.traiter_user_exception(exc_val)
191 CONTEXT.unset_current_step()
195 # sys_exc_typ,sys_exc_value,sys_exc_frame = sys_exc.info()
196 # (tuple de 3 éléments)
197 if CONTEXT.debug : traceback.print_exc()
199 exc_typ,exc_val,exc_fr=sys.exc_info()
200 l=traceback.format_exception(exc_typ,exc_val,exc_fr)
201 self.cr.exception("erreur non prevue et non traitee prevenir la maintenance "+
202 self.nom+'\n'+ string.join(l))
203 del exc_typ,exc_val,exc_fr
204 CONTEXT.unset_current_step()
206 def traiter_user_exception(self,exc_val):
207 """Cette methode realise un traitement sur les exceptions utilisateur
208 Par defaut il n'y a pas de traitement. La méthode doit etre
209 surchargée pour en introduire un.
213 def register(self,etape):
215 Cette méthode ajoute etape dans la liste des etapes : self.etapes
216 et retourne un numéro d'enregistrement
218 self.etapes.append(etape)
219 return self.g_register(etape)
221 def o_register(self,sd):
223 Retourne un identificateur pour concept
226 nom=sd.idracine + self.SEP + `self.nsd`
229 def g_register(self,etape):
231 Retourne un identificateur pour etape
233 self.nstep=self.nstep+1
234 idetape=etape.idracine + self.SEP + `self.nstep`
237 def create_sdprod(self,etape,nomsd):
239 Intention : Cette methode doit fabriquer le concept produit retourne
240 par l'etape etape et le nommer.
241 Elle est appelée à l'initiative de l'etape
242 pendant le processus de construction de cette etape :
243 methode __call__ de la classe CMD (OPER ou MACRO)
244 Ce travail est réalisé par le contexte supérieur
245 (etape.parent) car dans certains cas, le concept ne doit
246 pas etre fabriqué mais l'etape doit simplement utiliser
247 un concept préexistant.
248 Cas 1 : etape.reuse != None : le concept est réutilisé
249 Cas 2 : l'étape appartient à une macro qui a déclaré un
250 concept de sortie qui doit etre produit par cette
252 Dans le cas du JDC, le deuxième cas ne peut pas se produire.
254 sd= etape.get_sd_prod()
255 if sd != None and (etape.definition.reentrant == 'n' or etape.reuse is None) :
256 # ATTENTION : On ne nomme la SD que dans le cas de non reutilisation
257 # d un concept. Commande non reentrante ou reuse absent.
258 self.NommerSdprod(sd,nomsd)
261 def NommerSdprod(self,sd,sdnom,restrict='non'):
263 Nomme la SD apres avoir verifie que le nommage est possible : nom
265 Si le nom est deja utilise, leve une exception
266 Met le concept créé dans le concept global g_context
268 if CONTEXT.debug : print "JDC.NommerSdprod ",sd,sdnom
270 o=self.sds_dict.get(sdnom,None)
271 if isinstance(o,ASSD):
272 raise AsException("Nom de concept deja defini : %s" % sdnom)
274 # ATTENTION : Il ne faut pas ajouter sd dans sds car il s y trouve deja.
275 # Ajoute a la creation (appel de reg_sd).
276 self.sds_dict[sdnom]=sd
279 # En plus si restrict vaut 'non', on insere le concept dans le contexte du JDC
280 if restrict == 'non':
281 self.g_context[sdnom]=sd
285 Methode appelee dans l __init__ d un ASSD lors de sa creation
289 return self.o_register(sd)
291 def delete_concept_after_etape(self,etape,sd):
293 Met à jour les étapes du JDC qui sont après etape suite à
294 la disparition du concept sd
296 # Cette methode est définie dans le noyau mais ne sert que pendant
297 # la phase de creation des etapes et des concepts. Il n'y a aucun
298 # traitement particulier à réaliser.
299 # Dans d'autres conditions, il faut surcharger cette méthode
303 N_OBJECT.OBJECT.supprime(self)
304 for etape in self.etapes:
307 def get_file(self,unite=None,fic_origine=''):
309 Retourne le nom du fichier correspondant à un numero d'unité
310 logique (entier) ainsi que le source contenu dans le fichier
313 # Si le JDC est relié à une application maitre, on délègue la recherche
314 file,text= self.appli.get_file(unite,fic_origine)
318 if os.path.exists("fort."+str(unite)):
319 file= "fort."+str(unite)
321 raise AsException("Impossible de trouver le fichier correspondant"
322 " a l unite %s" % unite)
323 if not os.path.exists(file):
324 raise AsException("%s n'est pas un fichier existant" % unite)
328 if file == None : return None,None
329 text=string.replace(text,'\r\n','\n')
330 linecache.cache[file]=0,0,string.split(text,'\n'),file
333 def set_par_lot(self,par_lot):
335 Met le mode de traitement a PAR LOT
336 ou a COMMANDE par COMMANDE
337 en fonction de la valeur du mot cle PAR_LOT et
338 du contexte : application maitre ou pas
340 if self.appli == None:
341 # Pas d application maitre
344 # Avec application maitre
347 def accept(self,visitor):
349 Cette methode permet de parcourir l'arborescence des objets
350 en utilisant le pattern VISITEUR
352 visitor.visitJDC(self)
356 Cette methode a pour fonction d'ouvrir un interpreteur
357 pour que l'utilisateur entre des commandes interactivement
359 CONTEXT.set_current_step(self)
361 # Le module nommage utilise le module linecache pour accéder
362 # au source des commandes du jeu de commandes.
363 # Dans le cas d'un fichier, on accède au contenu de ce fichier
364 # Dans le cas de la console interactive, il faut pouvoir accéder
365 # aux commandes qui sont dans le buffer de la console
366 import linecache,code
367 console= code.InteractiveConsole(self.g_context,filename="<console>")
368 linecache.cache["<console>"]=0,0,console.buffer,"<console>"
369 banner="""***********************************************
370 * Interpreteur interactif %s
371 ***********************************************""" % self.code
372 console.interact(banner)
375 CONTEXT.unset_current_step()
377 def get_contexte_avant(self,etape):
379 Retourne le dictionnaire des concepts connus avant etape
380 On tient compte des commandes qui modifient le contexte
381 comme DETRUIRE ou les macros
382 Si etape == None, on retourne le contexte en fin de JDC
384 # L'étape courante pour laquelle le contexte a été calculé est
385 # mémorisée dans self.index_etape_courante
386 # XXX on pourrait faire mieux dans le cas PAR_LOT="NON" : en
388 # courante pendant le processus de construction des étapes.
389 # Si on insère des commandes (par ex, dans EFICAS), il faut préalablement
390 # remettre ce pointeur à 0
392 index_etape=self.etapes.index(etape)
394 index_etape=len(self.etapes)
395 if index_etape >= self.index_etape_courante:
396 # On calcule le contexte en partant du contexte existant
397 d=self.current_context
398 if self.index_etape_courante==0 and self.context_ini:
399 d.update(self.context_ini)
400 liste_etapes=self.etapes[self.index_etape_courante:index_etape]
402 d=self.current_context={}
403 if self.context_ini:d.update(self.context_ini)
404 liste_etapes=self.etapes
406 for e in liste_etapes:
411 self.index_etape_courante=index_etape
414 def get_global_contexte(self):
415 return self.g_context.copy()
417 def get_cmd(self,nomcmd):
419 Méthode pour recuperer la definition d'une commande
420 donnee par son nom dans les catalogues declares
423 for cata in self.cata:
424 if hasattr(cata,nomcmd):
425 return getattr(cata,nomcmd)