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 \
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 \
157 commandes en cours ...')
158 # On sauve le contexte pour garder la memoire des constantes
159 # En mode edition (EFICAS) ou lors des verifications le contexte
161 # mais les constantes sont perdues
162 self.const_context=self.g_context
163 exec self.proc_compile in self.g_context
165 CONTEXT.unset_current_step()
166 if self.appli != None : self.appli.affiche_infos('')
169 # Exception utilise pour interrompre un jeu
170 # de commandes avant la fin
171 # Fonctionnement normal, ne doit pas etre considere comme une erreur
172 CONTEXT.unset_current_step()
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()
197 # sys_exc_typ,sys_exc_value,sys_exc_frame = sys_exc.info()
198 # (tuple de 3 éléments)
199 if CONTEXT.debug : traceback.print_exc()
201 exc_typ,exc_val,exc_fr=sys.exc_info()
202 l=traceback.format_exception(exc_typ,exc_val,exc_fr)
203 self.cr.exception("erreur non prevue et non traitee prevenir la maintenance "+
204 self.nom+'\n'+ string.join(l))
205 del exc_typ,exc_val,exc_fr
206 CONTEXT.unset_current_step()
208 def traiter_user_exception(self,exc_val):
209 """Cette methode realise un traitement sur les exceptions utilisateur
210 Par defaut il n'y a pas de traitement. La méthode doit etre
211 surchargée pour en introduire un.
215 def register(self,etape):
217 Cette méthode ajoute etape dans la liste des etapes : self.etapes
218 et retourne un numéro d'enregistrement
220 self.etapes.append(etape)
221 return self.g_register(etape)
223 def o_register(self,sd):
225 Retourne un identificateur pour concept
228 nom=sd.idracine + self.SEP + `self.nsd`
231 def g_register(self,etape):
233 Retourne un identificateur pour etape
235 self.nstep=self.nstep+1
236 idetape=etape.idracine + self.SEP + `self.nstep`
239 def create_sdprod(self,etape,nomsd):
241 Intention : Cette methode doit fabriquer le concept produit retourne
242 par l'etape etape et le nommer.
243 Elle est appelée à l'initiative de l'etape
244 pendant le processus de construction de cette etape :
245 methode __call__ de la classe CMD (OPER ou MACRO)
246 Ce travail est réalisé par le contexte supérieur
247 (etape.parent) car dans certains cas, le concept ne doit
248 pas etre fabriqué mais l'etape doit simplement utiliser
249 un concept préexistant.
250 Cas 1 : etape.reuse != None : le concept est réutilisé
251 Cas 2 : l'étape appartient à une macro qui a déclaré un
252 concept de sortie qui doit etre produit par cette
254 Dans le cas du JDC, le deuxième cas ne peut pas se produire.
256 sd= etape.get_sd_prod()
257 if sd != None and (etape.definition.reentrant == 'n' or etape.reuse is None) :
258 # ATTENTION : On ne nomme la SD que dans le cas de non reutilisation
259 # d un concept. Commande non reentrante ou reuse absent.
260 self.NommerSdprod(sd,nomsd)
263 def NommerSdprod(self,sd,sdnom,restrict='non'):
265 Nomme la SD apres avoir verifie que le nommage est possible : nom
267 Si le nom est deja utilise, leve une exception
268 Met le concept créé dans le concept global g_context
270 if CONTEXT.debug : print "JDC.NommerSdprod ",sd,sdnom
272 o=self.sds_dict.get(sdnom,None)
273 if isinstance(o,ASSD):
274 raise AsException("Nom de concept deja defini : %s" % sdnom)
276 # ATTENTION : Il ne faut pas ajouter sd dans sds car il s y trouve deja.
277 # Ajoute a la creation (appel de reg_sd).
278 self.sds_dict[sdnom]=sd
281 # En plus si restrict vaut 'non', on insere le concept dans le contexte du JDC
282 if restrict == 'non':
283 self.g_context[sdnom]=sd
287 Methode appelee dans l __init__ d un ASSD lors de sa creation
291 return self.o_register(sd)
293 def delete_concept_after_etape(self,etape,sd):
295 Met à jour les étapes du JDC qui sont après etape suite à
296 la disparition du concept sd
298 # Cette methode est définie dans le noyau mais ne sert que pendant
299 # la phase de creation des etapes et des concepts. Il n'y a aucun
300 # traitement particulier à réaliser.
301 # Dans d'autres conditions, il faut surcharger cette méthode
305 N_OBJECT.OBJECT.supprime(self)
306 for etape in self.etapes:
309 def get_file(self,unite=None,fic_origine=''):
311 Retourne le nom du fichier correspondant à un numero d'unité
312 logique (entier) ainsi que le source contenu dans le fichier
315 # Si le JDC est relié à une application maitre, on délègue la recherche
316 file,text= self.appli.get_file(unite,fic_origine)
320 if os.path.exists("fort."+str(unite)):
321 file= "fort."+str(unite)
323 raise AsException("Impossible de trouver le fichier correspondant"
324 " a l unite %s" % unite)
325 if not os.path.exists(file):
326 raise AsException("%s n'est pas un fichier existant" % unite)
330 if file == None : return None,None
331 text=string.replace(text,'\r\n','\n')
332 linecache.cache[file]=0,0,string.split(text,'\n'),file
335 def set_par_lot(self,par_lot):
337 Met le mode de traitement a PAR LOT
338 ou a COMMANDE par COMMANDE
339 en fonction de la valeur du mot cle PAR_LOT et
340 du contexte : application maitre ou pas
342 if self.appli == None:
343 # Pas d application maitre
346 # Avec application maitre
349 def accept(self,visitor):
351 Cette methode permet de parcourir l'arborescence des objets
352 en utilisant le pattern VISITEUR
354 visitor.visitJDC(self)
358 Cette methode a pour fonction d'ouvrir un interpreteur
359 pour que l'utilisateur entre des commandes interactivement
361 CONTEXT.set_current_step(self)
363 # Le module nommage utilise le module linecache pour accéder
364 # au source des commandes du jeu de commandes.
365 # Dans le cas d'un fichier, on accède au contenu de ce fichier
366 # Dans le cas de la console interactive, il faut pouvoir accéder
367 # aux commandes qui sont dans le buffer de la console
368 import linecache,code
369 console= code.InteractiveConsole(self.g_context,filename="<console>")
370 linecache.cache["<console>"]=0,0,console.buffer,"<console>"
371 banner="""***********************************************
372 * Interpreteur interactif %s
373 ***********************************************""" % self.code
374 console.interact(banner)
377 CONTEXT.unset_current_step()
379 def get_contexte_avant(self,etape):
381 Retourne le dictionnaire des concepts connus avant etape
382 On tient compte des commandes qui modifient le contexte
383 comme DETRUIRE ou les macros
384 Si etape == None, on retourne le contexte en fin de JDC
386 # L'étape courante pour laquelle le contexte a été calculé est
387 # mémorisée dans self.index_etape_courante
388 # XXX on pourrait faire mieux dans le cas PAR_LOT="NON" : en
390 # courante pendant le processus de construction des étapes.
391 # Si on insère des commandes (par ex, dans EFICAS), il faut préalablement
392 # remettre ce pointeur à 0
394 index_etape=self.etapes.index(etape)
396 index_etape=len(self.etapes)
397 if index_etape >= self.index_etape_courante:
398 # On calcule le contexte en partant du contexte existant
399 d=self.current_context
400 if self.index_etape_courante==0 and self.context_ini:
401 d.update(self.context_ini)
402 liste_etapes=self.etapes[self.index_etape_courante:index_etape]
404 d=self.current_context={}
405 if self.context_ini:d.update(self.context_ini)
406 liste_etapes=self.etapes
408 for e in liste_etapes:
413 self.index_etape_courante=index_etape
416 def get_global_contexte(self):
417 return self.g_context.copy()
419 def get_cmd(self,nomcmd):
421 Méthode pour recuperer la definition d'une commande
422 donnee par son nom dans les catalogues declares
425 for cata in self.cata:
426 if hasattr(cata,nomcmd):
427 return getattr(cata,nomcmd)