2 Ce module contient la classe JDC qui sert à interpréter un jeu de commandes
6 import os,string,traceback
7 import types,sys,linecache
12 from N_Exception import AsException
13 from N_ASSD import ASSD
15 class JDC(N_OBJECT.OBJECT):
17 Cette classe interprete un jeu de commandes fourni sous
18 la forme d'une chaine de caractères
22 Attributs d'instance :
33 from N_utils import SEP
35 def __init__(self,definition=None,procedure=None,cata=None,
36 cata_ord_dico=None,parent=None,
37 nom='SansNom',appli=None,context_ini=None,**args):
38 self.procedure=procedure
39 self.definition = definition
41 if type(self.cata) != types.TupleType and cata != None:
42 self.cata=(self.cata,)
43 self.cata_ordonne_dico=cata_ord_dico
47 self.context_ini=context_ini
48 # On conserve les arguments supplémentaires. Il est possible de passer
49 # des informations globales au JDC par ce moyen. Il pourrait etre plus
50 # sur de mettre en place le mecanisme des mots-cles pour verifier la
51 # validité des valeurs passées.
53 # On initialise avec les parametres de la definition puis on
54 # update avec ceux du JDC
55 self.args=self.definition.args
56 self.args.update(args)
61 self.regles=definition.regles
62 self.code = definition.code
67 # Creation de l objet compte rendu pour collecte des erreurs
69 self.cr = self.CR(debut = "CR phase d'initialisation",
70 fin = "fin CR phase d'initialisation")
75 self.current_context={}
76 self.index_etape_courante=0
80 Cette methode compile la chaine procedure
81 Si des erreurs se produisent, elles sont consignées dans le
85 if self.appli != None :
86 self.appli.affiche_infos('Compilation du fichier de commandes \
88 self.proc_compile=compile(self.procedure,self.nom,'exec')
90 if CONTEXT.debug : traceback.print_exc()
91 l=traceback.format_exception_only(SyntaxError,e)
92 self.cr.exception("Compilation impossible : "+string.join(l))
95 def exec_compile(self):
97 Cette méthode execute le jeu de commandes compilé dans le contexte
98 self.g_context de l'objet JDC
100 CONTEXT.set_current_step(self)
101 # Le module nommage utilise le module linecache pour accéder
102 # au source des commandes du jeu de commandes.
103 # Dans le cas d'un fichier, on accède au contenu de ce fichier
104 # Dans le cas d'une chaine de caractères il faut accéder
105 # aux commandes qui sont dans la chaine
107 linecache.cache[self.nom]=0,0,string.split(self.procedure,'\n'),self.nom
109 exec self.exec_init in self.g_context
110 for obj_cata in self.cata:
111 if type(obj_cata) == types.ModuleType :
112 init2 = "from "+obj_cata.__name__+" import *"
113 exec init2 in self.g_context
114 # Si l'attribut context_ini n'est pas vide, on ajoute au contexte global
115 # le contexte initial (--> permet d'évaluer un JDC en récupérant un contexte
116 # d'un autre par exemple)
117 if self.context_ini :
118 self.g_context.update(self.context_ini)
120 if self.appli != None :
121 self.appli.affiche_infos('Interprétation du fichier de \
122 commandes en cours ...')
123 # On sauve le contexte pour garder la memoire des constantes
124 # En mode edition (EFICAS) ou lors des verifications le contexte
126 # mais les constantes sont perdues
127 self.const_context=self.g_context
128 exec self.proc_compile in self.g_context
130 CONTEXT.unset_current_step()
131 if self.appli != None : self.appli.affiche_infos('')
134 # Exception utilise pour interrompre un jeu
135 # de commandes avant la fin
136 # Fonctionnement normal, ne doit pas etre considere comme une erreur
137 CONTEXT.unset_current_step()
139 except AsException,e:
140 # une erreur a ete identifiee
142 traceback.print_exc()
143 self.cr.exception(str(e))
144 CONTEXT.unset_current_step()
147 etype, value, tb = sys.exc_info()
148 l= traceback.extract_tb(tb)
149 s= traceback.format_exception_only("Erreur de nom",e)[0][:-1]
150 message = "erreur de syntaxe, %s ligne %d" % (s,l[-1][1])
153 traceback.print_exc()
154 self.cr.exception(message)
155 CONTEXT.unset_current_step()
159 # sys_exc_typ,sys_exc_value,sys_exc_frame = sys_exc.info()
160 # (tuple de 3 éléments)
162 traceback.print_exc()
163 #prbanner("erreur non prevue et non traitee prevenir \
164 # la maintenance "+self.nom)
165 l=traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],
167 self.cr.exception("erreur non prevue et non traitee prevenir la maintenance "+
168 self.nom+'\n'+ string.join(l))
169 CONTEXT.unset_current_step()
171 def register(self,etape):
173 Cette méthode ajoute etape dans la liste des etapes : self.etapes
174 et retourne un numéro d'enregistrement
176 self.etapes.append(etape)
177 return self.g_register(etape)
179 def o_register(self,sd):
181 Retourne un identificateur pour concept
184 nom=sd.idracine + self.SEP + `self.nsd`
187 def g_register(self,etape):
189 Retourne un identificateur pour etape
191 self.nstep=self.nstep+1
192 idetape=etape.idracine + self.SEP + `self.nstep`
195 def create_sdprod(self,etape,nomsd):
197 Intention : Cette methode doit fabriquer le concept produit retourne
198 par l'etape etape et le nommer.
199 Elle est appelée à l'initiative de l'etape
200 pendant le processus de construction de cette etape :
201 methode __call__ de la classe CMD (OPER ou MACRO)
202 Ce travail est réalisé par le contexte supérieur
203 (etape.parent) car dans certains cas, le concept ne doit
204 pas etre fabriqué mais l'etape doit simplement utiliser
205 un concept préexistant.
206 Cas 1 : etape.reuse != None : le concept est réutilisé
207 Cas 2 : l'étape appartient à une macro qui a déclaré un
208 concept de sortie qui doit etre produit par cette
210 Dans le cas du JDC, le deuxième cas ne peut pas se produire.
212 sd= etape.get_sd_prod()
213 if sd != None and etape.reuse == None:
214 # ATTENTION : On ne nomme la SD que dans le cas de non reutilisation
216 self.NommerSdprod(sd,nomsd)
219 def NommerSdprod(self,sd,sdnom):
221 Nomme la SD apres avoir verifie que le nommage est possible : nom
223 Si le nom est deja utilise, leve une exception
224 Met le concept créé dans le concept global g_context
226 if CONTEXT.debug : print "JDC.NommerSdprod ",sd,sdnom
227 o=self.g_context.get(sdnom,None)
228 if isinstance(o,ASSD):
229 raise AsException("Nom de concept deja defini : %s" % sdnom)
231 # ATTENTION : Il ne faut pas ajouter sd dans sds car il s y trouve deja.
232 # Ajoute a la creation (appel de reg_sd).
233 self.g_context[sdnom]=sd
238 Methode appelee dans l __init__ d un ASSD lors de sa creation
242 return self.o_register(sd)
244 def delete_concept_after_etape(self,etape,sd):
246 Met à jour les étapes du JDC qui sont après etape suite à
247 la disparition du concept sd
249 # Cette methode est définie dans le noyau mais ne sert que pendant
250 # la phase de creation des etapes et des concepts. Il n'y a aucun
251 # traitement particulier à réaliser.
252 # Dans d'autres conditions, il faut surcharger cette méthode
256 N_OBJECT.OBJECT.supprime(self)
257 for etape in self.etapes:
260 def get_file(self,unite=None,fic_origine=''):
262 Retourne le nom du fichier correspondant à un numero d'unité
263 logique (entier) ainsi que le source contenu dans le fichier
266 # Si le JDC est relié à une application maitre, on délègue la recherche
267 file= self.appli.get_file(unite,fic_origine)
271 if os.path.exists("fort."+str(unite)):
272 file= "fort."+str(unite)
274 raise AsException("Impossible de trouver le fichier correspondant \
275 a l unite %s" % unite)
276 if not os.path.exists(file):
277 raise AsException("%s n'est pas un fichier existant" % unite)
279 text=string.replace(fproc.read(),'\r\n','\n')
281 linecache.cache[file]=0,0,string.split(text,'\n'),file
284 def set_par_lot(self,par_lot):
286 Met le mode de traitement a PAR LOT
287 ou a COMMANDE par COMMANDE
288 en fonction de la valeur du mot cle PAR_LOT et
289 du contexte : application maitre ou pas
291 if self.appli == None:
292 # Pas d application maitre
295 # Avec application maitre
298 def accept(self,visitor):
300 Cette methode permet de parcourir l'arborescence des objets
301 en utilisant le pattern VISITEUR
303 visitor.visitJDC(self)
307 Cette methode a pour fonction d'ouvrir un interpreteur
308 pour que l'utilisateur entre des commandes interactivement
310 CONTEXT.set_current_step(self)
312 # Le module nommage utilise le module linecache pour accéder
313 # au source des commandes du jeu de commandes.
314 # Dans le cas d'un fichier, on accède au contenu de ce fichier
315 # Dans le cas de la console interactive, il faut pouvoir accéder
316 # aux commandes qui sont dans le buffer de la console
317 import linecache,code
318 console= code.InteractiveConsole(self.g_context,filename="<console>")
319 linecache.cache["<console>"]=0,0,console.buffer,"<console>"
320 banner="""***********************************************
321 * Interpreteur interactif %s
322 ***********************************************""" % self.code
323 console.interact(banner)
326 CONTEXT.unset_current_step()
328 def get_contexte_avant(self,etape):
330 Retourne le dictionnaire des concepts connus avant etape
331 On tient compte des commandes qui modifient le contexte
332 comme DETRUIRE ou les macros
333 Si etape == None, on retourne le contexte en fin de JDC
335 # L'étape courante pour laquelle le contexte a été calculé est
336 # mémorisée dans self.index_etape_courante
337 # XXX on pourrait faire mieux dans le cas PAR_LOT="NON" : en
339 # courante pendant le processus de construction des étapes.
340 # Si on insère des commandes (par ex, dans EFICAS), il faut préalablement
341 # remettre ce pointeur à 0
343 index_etape=self.etapes.index(etape)
345 index_etape=len(self.etapes)
346 if index_etape >= self.index_etape_courante:
347 # On calcule le contexte en partant du contexte existant
348 d=self.current_context
349 liste_etapes=self.etapes[self.index_etape_courante:index_etape]
351 d=self.current_context={}
352 liste_etapes=self.etapes
354 for e in liste_etapes:
359 self.index_etape_courante=index_etape