Salome HOME
CCAR:amelioration de la fonctionnalité mots clés inconnus
[tools/eficas.git] / Noyau / N_JDC.py
1 """
2    Ce module contient la classe JDC qui sert à interpréter un jeu de commandes
3 """
4
5 # Modules Python
6 import os,string,traceback
7 import types,sys,linecache
8
9 # Modules EFICAS
10 import N_OBJECT
11 import N_CR
12 from N_Exception import AsException
13 from N_ASSD import ASSD
14
15 class JDC(N_OBJECT.OBJECT):
16    """
17       Cette classe interprete un jeu de commandes fourni sous
18       la forme d'une chaine de caractères
19
20       Attributs de classe :
21
22       Attributs d'instance :
23
24    """
25    nature = "JDC"
26    CR=N_CR.CR
27    exec_init="""
28 import Accas
29 from Accas import _F
30 from Accas import *
31 NONE = None
32 """
33    from N_utils import SEP
34
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
40       self.cata=cata
41       if type(self.cata) != types.TupleType and cata != None: 
42          self.cata=(self.cata,)
43       self.cata_ordonne_dico=cata_ord_dico
44       self.nom = nom
45       self.appli=appli
46       self.parent=parent
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.
52       # Ceci reste à faire
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)
57       self.nstep=0
58       self.nsd=0
59       self.par_lot='OUI'
60       if definition:
61          self.regles=definition.regles
62          self.code = definition.code
63       else:
64          self.regles=()
65          self.code = "CODE"
66       #
67       #  Creation de l objet compte rendu pour collecte des erreurs
68       #
69       self.cr = self.CR(debut = "CR phase d'initialisation", 
70                         fin = "fin CR phase d'initialisation")
71       self.g_context={}
72       self.sds=[]
73       self.etapes=[]
74       self.mc_globaux={}
75       self.current_context={}
76       self.index_etape_courante=0
77
78    def compile(self):
79       """
80          Cette methode compile la chaine procedure
81          Si des erreurs se produisent, elles sont consignées dans le 
82          compte-rendu self.cr
83       """
84       try:
85         if self.appli != None : 
86            self.appli.affiche_infos('Compilation du fichier de commandes \
87                                      en cours ...')
88         self.proc_compile=compile(self.procedure,self.nom,'exec')
89       except SyntaxError,e:
90         if CONTEXT.debug : traceback.print_exc()
91         l=traceback.format_exception_only(SyntaxError,e)
92         self.cr.exception("Compilation impossible : "+string.join(l))
93       return
94
95    def exec_compile(self):
96       """
97          Cette méthode execute le jeu de commandes compilé dans le contexte
98          self.g_context de l'objet JDC
99       """
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
106       import linecache
107       linecache.cache[self.nom]=0,0,string.split(self.procedure,'\n'),self.nom
108       try:
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)
119
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 
125          # est recalculé
126          # mais les constantes sont perdues
127          self.const_context=self.g_context
128          exec self.proc_compile in self.g_context
129
130          CONTEXT.unset_current_step()
131          if self.appli != None : self.appli.affiche_infos('')
132
133       except EOFError:
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()
138
139       except AsException,e:
140         # une erreur a ete identifiee
141         if CONTEXT.debug :
142           traceback.print_exc()
143         self.cr.exception(str(e))
144         CONTEXT.unset_current_step()
145
146       except NameError,e:
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])
151         if CONTEXT.debug :
152           #prbanner(message)
153           traceback.print_exc()
154         self.cr.exception(message)
155         CONTEXT.unset_current_step()
156
157       except :
158         # erreur inattendue
159         # sys_exc_typ,sys_exc_value,sys_exc_frame = sys_exc.info() 
160         # (tuple de 3 éléments)
161         if CONTEXT.debug :
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],
166                                      sys.exc_info()[2])
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()
170
171    def register(self,etape):
172       """
173          Cette méthode ajoute etape dans la liste des etapes : self.etapes
174          et retourne un numéro d'enregistrement
175       """
176       self.etapes.append(etape)
177       return self.g_register(etape)
178
179    def o_register(self,sd):
180       """
181          Retourne un identificateur pour concept
182       """
183       self.nsd=self.nsd+1
184       nom=sd.idracine + self.SEP + `self.nsd`
185       return nom
186
187    def g_register(self,etape):
188       """
189           Retourne un identificateur pour etape
190       """
191       self.nstep=self.nstep+1
192       idetape=etape.idracine + self.SEP + `self.nstep`
193       return idetape
194
195    def create_sdprod(self,etape,nomsd):
196       """ 
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 
209                           etape.
210                   Dans le cas du JDC, le deuxième cas ne peut pas se produire.
211       """
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 
215          # d un concept
216          self.NommerSdprod(sd,nomsd)
217       return sd
218
219    def NommerSdprod(self,sd,sdnom):
220       """ 
221           Nomme la SD apres avoir verifie que le nommage est possible : nom 
222           non utilise
223           Si le nom est deja utilise, leve une exception
224           Met le concept créé dans le concept global g_context
225       """
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)
230
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
234       sd.nom=sdnom
235
236    def reg_sd(self,sd):
237       """ 
238           Methode appelee dans l __init__ d un ASSD lors de sa creation 
239           pour s enregistrer
240       """
241       self.sds.append(sd)
242       return self.o_register(sd)
243
244    def delete_concept_after_etape(self,etape,sd):
245       """
246           Met à jour les étapes du JDC qui sont après etape suite à
247           la disparition du concept sd
248       """
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
253       return
254
255    def supprime(self):
256       N_OBJECT.OBJECT.supprime(self)
257       for etape in self.etapes:
258          etape.supprime()
259
260    def get_file(self,unite=None,fic_origine=''):
261       """
262           Retourne le nom du fichier correspondant à un numero d'unité 
263           logique (entier) ainsi que le source contenu dans le fichier
264       """
265       if self.appli :
266          # Si le JDC est relié à une application maitre, on délègue la recherche
267          file= self.appli.get_file(unite,fic_origine)
268       else:
269          file = None
270          if unite != None:
271             if os.path.exists("fort."+str(unite)):
272                file= "fort."+str(unite)
273          if file == None :
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)
278       fproc=open(file,'r')
279       text=string.replace(fproc.read(),'\r\n','\n')
280       fproc.close()
281       linecache.cache[file]=0,0,string.split(text,'\n'),file
282       return file,text
283
284    def set_par_lot(self,par_lot):
285       """ 
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
290       """
291       if self.appli == None:
292         # Pas d application maitre
293         self.par_lot=par_lot
294       else:
295         # Avec application maitre
296         self.par_lot='OUI'
297
298    def accept(self,visitor):
299       """
300          Cette methode permet de parcourir l'arborescence des objets
301          en utilisant le pattern VISITEUR
302       """
303       visitor.visitJDC(self)
304
305    def interact(self):
306       """
307           Cette methode a pour fonction d'ouvrir un interpreteur 
308           pour que l'utilisateur entre des commandes interactivement
309       """
310       CONTEXT.set_current_step(self)
311       try:
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)
324       finally:
325          console=None
326          CONTEXT.unset_current_step()
327
328    def get_contexte_avant(self,etape):
329       """
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
334       """
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 
338       # mémorisant l'étape
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
342       if etape:
343          index_etape=self.etapes.index(etape)
344       else:
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]
350       else:
351          d=self.current_context={}
352          liste_etapes=self.etapes
353
354       for e in liste_etapes:
355          if e is etape:
356             break
357          if e.isactif():
358             e.update_context(d)
359       self.index_etape_courante=index_etape
360       return d
361