1 # -*- coding: utf-8 -*-
2 #@ MODIF N_JDC Noyau DATE 18/05/2004 AUTEUR DURAND C.DURAND
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 # ======================================================================
23 Ce module contient la classe JDC qui sert à interpréter un jeu de commandes
27 import os,string,traceback
28 import types,sys,linecache
33 from N_Exception import AsException
34 from N_ASSD import ASSD
36 class JDC(N_OBJECT.OBJECT):
38 Cette classe interprete un jeu de commandes fourni sous
39 la forme d'une chaine de caractères
43 Attributs d'instance :
55 from N_utils import SEP
57 def __init__(self,definition=None,procedure=None,cata=None,
58 cata_ord_dico=None,parent=None,
59 nom='SansNom',appli=None,context_ini=None,**args):
60 self.procedure=procedure
61 self.definition = definition
63 if type(self.cata) != types.TupleType and cata != None:
64 self.cata=(self.cata,)
65 self.cata_ordonne_dico=cata_ord_dico
69 self.context_ini=context_ini
70 # On conserve les arguments supplémentaires. Il est possible de passer
71 # des informations globales au JDC par ce moyen. Il pourrait etre plus
72 # sur de mettre en place le mecanisme des mots-cles pour verifier la
73 # validité des valeurs passées.
75 # On initialise avec les parametres de la definition puis on
76 # update avec ceux du JDC
77 self.args=self.definition.args
78 self.args.update(args)
83 self.regles=definition.regles
84 self.code = definition.code
89 # Creation de l objet compte rendu pour collecte des erreurs
91 self.cr = self.CR(debut = "CR phase d'initialisation",
92 fin = "fin CR phase d'initialisation")
94 # Liste pour stocker tous les concepts produits dans le JDC
96 # Dictionnaire pour stocker tous les concepts du JDC (acces rapide par le nom)
100 self.current_context={}
101 self.condition_context={}
102 self.index_etape_courante=0
103 self.UserError="UserError"
108 Cette methode compile la chaine procedure
109 Si des erreurs se produisent, elles sont consignées dans le
113 if self.appli != None :
114 self.appli.affiche_infos('Compilation du fichier de commandes en cours ...')
115 self.proc_compile=compile(self.procedure,self.nom,'exec')
116 except SyntaxError,e:
117 if CONTEXT.debug : traceback.print_exc()
118 l=traceback.format_exception_only(SyntaxError,e)
119 self.cr.exception("Compilation impossible : "+string.join(l))
122 def exec_compile(self):
124 Cette méthode execute le jeu de commandes compilé dans le contexte
125 self.g_context de l'objet JDC
127 CONTEXT.set_current_step(self)
128 # Le module nommage utilise le module linecache pour accéder
129 # au source des commandes du jeu de commandes.
130 # Dans le cas d'un fichier, on accède au contenu de ce fichier
131 # Dans le cas d'une chaine de caractères il faut accéder
132 # aux commandes qui sont dans la chaine
134 linecache.cache[self.nom]=0,0,string.split(self.procedure,'\n'),self.nom
136 exec self.exec_init in self.g_context
137 for obj_cata in self.cata:
138 if type(obj_cata) == types.ModuleType :
139 init2 = "from "+obj_cata.__name__+" import *"
140 exec init2 in self.g_context
142 # Initialisation du contexte global pour l'évaluation des conditions de BLOC
143 # On utilise une copie de l'initialisation du contexte du jdc
144 self.condition_context=self.g_context.copy()
146 # Si l'attribut context_ini n'est pas vide, on ajoute au contexte global
147 # le contexte initial (--> permet d'évaluer un JDC en récupérant un contexte
148 # d'un autre par exemple)
149 if self.context_ini :
150 self.g_context.update(self.context_ini)
151 # Update du dictionnaire des concepts
152 for sdnom,sd in self.context_ini.items():
153 if isinstance(sd,ASSD):self.sds_dict[sdnom]=sd
155 if self.appli != None :
156 self.appli.affiche_infos('Interprétation du fichier de commandes en cours ...')
157 # On sauve le contexte pour garder la memoire des constantes
158 # En mode edition (EFICAS) ou lors des verifications le contexte
160 # mais les constantes sont perdues
161 self.const_context=self.g_context
162 exec self.proc_compile in self.g_context
164 CONTEXT.unset_current_step()
165 if self.appli != None : self.appli.affiche_infos('')
168 # Exception utilise pour interrompre un jeu
169 # de commandes avant la fin
170 # Fonctionnement normal, ne doit pas etre considere comme une erreur
171 CONTEXT.unset_current_step()
172 self.traiter_fin_exec('commande')
174 except AsException,e:
175 # une erreur a ete identifiee
177 traceback.print_exc()
178 self.cr.exception(str(e))
179 CONTEXT.unset_current_step()
182 etype, value, tb = sys.exc_info()
183 l= traceback.extract_tb(tb)
184 s= traceback.format_exception_only("Erreur de nom",e)[0][:-1]
185 message = "erreur de syntaxe, %s ligne %d" % (s,l[-1][1])
187 traceback.print_exc()
188 self.cr.exception(message)
189 CONTEXT.unset_current_step()
191 except self.UserError,exc_val:
192 self.traiter_user_exception(exc_val)
193 CONTEXT.unset_current_step()
194 self.traiter_fin_exec('commande')
198 # sys_exc_typ,sys_exc_value,sys_exc_frame = sys_exc.info()
199 # (tuple de 3 éléments)
200 if CONTEXT.debug : traceback.print_exc()
202 exc_typ,exc_val,exc_fr=sys.exc_info()
203 l=traceback.format_exception(exc_typ,exc_val,exc_fr)
204 self.cr.exception("erreur non prevue et non traitee prevenir la maintenance "+
205 self.nom+'\n'+ string.join(l))
206 del exc_typ,exc_val,exc_fr
207 CONTEXT.unset_current_step()
209 def traiter_fin_exec(self,mode,etape=None):
211 Cette methode realise un traitement final apres l'execution de toutes
212 les commandes en mode commande par commande ou par lot
213 Par defaut il n'y a pas de traitement. Elle doit etre surchargee
214 pour en introduire un
216 print "FIN D'EXECUTION",mode,etape
218 def traiter_user_exception(self,exc_val):
219 """Cette methode realise un traitement sur les exceptions utilisateur
220 Par defaut il n'y a pas de traitement. La méthode doit etre
221 surchargée pour en introduire un.
225 def register(self,etape):
227 Cette méthode ajoute etape dans la liste des etapes : self.etapes
228 et retourne un numéro d'enregistrement
230 self.etapes.append(etape)
231 return self.g_register(etape)
233 def o_register(self,sd):
235 Retourne un identificateur pour concept
238 nom=sd.idracine + self.SEP + `self.nsd`
241 def g_register(self,etape):
243 Retourne un identificateur pour etape
245 self.nstep=self.nstep+1
246 idetape=etape.idracine + self.SEP + `self.nstep`
249 def create_sdprod(self,etape,nomsd):
251 Intention : Cette methode doit fabriquer le concept produit retourne
252 par l'etape etape et le nommer.
253 Elle est appelée à l'initiative de l'etape
254 pendant le processus de construction de cette etape :
255 methode __call__ de la classe CMD (OPER ou MACRO)
256 Ce travail est réalisé par le contexte supérieur
257 (etape.parent) car dans certains cas, le concept ne doit
258 pas etre fabriqué mais l'etape doit simplement utiliser
259 un concept préexistant.
260 Cas 1 : etape.reuse != None : le concept est réutilisé
261 Cas 2 : l'étape appartient à une macro qui a déclaré un
262 concept de sortie qui doit etre produit par cette
264 Dans le cas du JDC, le deuxième cas ne peut pas se produire.
266 sd= etape.get_sd_prod()
267 if sd != None and (etape.definition.reentrant == 'n' or etape.reuse is None) :
268 # ATTENTION : On ne nomme la SD que dans le cas de non reutilisation
269 # d un concept. Commande non reentrante ou reuse absent.
270 self.NommerSdprod(sd,nomsd)
273 def NommerSdprod(self,sd,sdnom,restrict='non'):
275 Nomme la SD apres avoir verifie que le nommage est possible : nom
277 Si le nom est deja utilise, leve une exception
278 Met le concept créé dans le concept global g_context
280 if CONTEXT.debug : print "JDC.NommerSdprod ",sd,sdnom
282 o=self.sds_dict.get(sdnom,None)
283 if isinstance(o,ASSD):
284 raise AsException("Nom de concept deja defini : %s" % sdnom)
286 # ATTENTION : Il ne faut pas ajouter sd dans sds car il s y trouve deja.
287 # Ajoute a la creation (appel de reg_sd).
288 self.sds_dict[sdnom]=sd
291 # En plus si restrict vaut 'non', on insere le concept dans le contexte du JDC
292 if restrict == 'non':
293 self.g_context[sdnom]=sd
297 Methode appelee dans l __init__ d un ASSD lors de sa creation
301 return self.o_register(sd)
303 def delete_concept_after_etape(self,etape,sd):
305 Met à jour les étapes du JDC qui sont après etape suite à
306 la disparition du concept sd
308 # Cette methode est définie dans le noyau mais ne sert que pendant
309 # la phase de creation des etapes et des concepts. Il n'y a aucun
310 # traitement particulier à réaliser.
311 # Dans d'autres conditions, il faut surcharger cette méthode
315 N_OBJECT.OBJECT.supprime(self)
316 for etape in self.etapes:
319 def get_file(self,unite=None,fic_origine=''):
321 Retourne le nom du fichier correspondant à un numero d'unité
322 logique (entier) ainsi que le source contenu dans le fichier
325 # Si le JDC est relié à une application maitre, on délègue la recherche
326 file,text= self.appli.get_file(unite,fic_origine)
330 if os.path.exists("fort."+str(unite)):
331 file= "fort."+str(unite)
333 raise AsException("Impossible de trouver le fichier correspondant"
334 " a l unite %s" % unite)
335 if not os.path.exists(file):
336 raise AsException("%s n'est pas un fichier existant" % unite)
340 if file == None : return None,None
341 text=string.replace(text,'\r\n','\n')
342 linecache.cache[file]=0,0,string.split(text,'\n'),file
345 def set_par_lot(self,par_lot):
347 Met le mode de traitement a PAR LOT
348 ou a COMMANDE par COMMANDE
349 en fonction de la valeur du mot cle PAR_LOT et
350 du contexte : application maitre ou pas
352 if self.appli == None:
353 # Pas d application maitre
356 # Avec application maitre
359 def accept(self,visitor):
361 Cette methode permet de parcourir l'arborescence des objets
362 en utilisant le pattern VISITEUR
364 visitor.visitJDC(self)
368 Cette methode a pour fonction d'ouvrir un interpreteur
369 pour que l'utilisateur entre des commandes interactivement
371 CONTEXT.set_current_step(self)
373 # Le module nommage utilise le module linecache pour accéder
374 # au source des commandes du jeu de commandes.
375 # Dans le cas d'un fichier, on accède au contenu de ce fichier
376 # Dans le cas de la console interactive, il faut pouvoir accéder
377 # aux commandes qui sont dans le buffer de la console
378 import linecache,code
379 console= code.InteractiveConsole(self.g_context,filename="<console>")
380 linecache.cache["<console>"]=0,0,console.buffer,"<console>"
381 banner="""***********************************************
382 * Interpreteur interactif %s
383 ***********************************************""" % self.code
384 console.interact(banner)
387 CONTEXT.unset_current_step()
389 def get_contexte_avant(self,etape):
391 Retourne le dictionnaire des concepts connus avant etape
392 On tient compte des commandes qui modifient le contexte
393 comme DETRUIRE ou les macros
394 Si etape == None, on retourne le contexte en fin de JDC
396 # L'étape courante pour laquelle le contexte a été calculé est
397 # mémorisée dans self.index_etape_courante
398 # XXX on pourrait faire mieux dans le cas PAR_LOT="NON" : en
400 # courante pendant le processus de construction des étapes.
401 # Si on insère des commandes (par ex, dans EFICAS), il faut préalablement
402 # remettre ce pointeur à 0
404 index_etape=self.etapes.index(etape)
406 index_etape=len(self.etapes)
407 if index_etape >= self.index_etape_courante:
408 # On calcule le contexte en partant du contexte existant
409 d=self.current_context
410 if self.index_etape_courante==0 and self.context_ini:
411 d.update(self.context_ini)
412 liste_etapes=self.etapes[self.index_etape_courante:index_etape]
414 d=self.current_context={}
415 if self.context_ini:d.update(self.context_ini)
416 liste_etapes=self.etapes
418 for e in liste_etapes:
423 self.index_etape_courante=index_etape
426 def get_global_contexte(self):
427 return self.g_context.copy()
429 def get_cmd(self,nomcmd):
431 Méthode pour recuperer la definition d'une commande
432 donnee par son nom dans les catalogues declares
435 for cata in self.cata:
436 if hasattr(cata,nomcmd):
437 return getattr(cata,nomcmd)